]> git.openstreetmap.org Git - rails.git/blob - test/controllers/api/changesets_controller_test.rb
Merge remote-tracking branch 'upstream/pull/5694'
[rails.git] / test / controllers / api / changesets_controller_test.rb
1 require "test_helper"
2
3 module Api
4   class ChangesetsControllerTest < ActionDispatch::IntegrationTest
5     ##
6     # test all routes which lead to this controller
7     def test_routes
8       assert_routing(
9         { :path => "/api/0.6/changesets", :method => :get },
10         { :controller => "api/changesets", :action => "index" }
11       )
12       assert_routing(
13         { :path => "/api/0.6/changesets.json", :method => :get },
14         { :controller => "api/changesets", :action => "index", :format => "json" }
15       )
16       assert_routing(
17         { :path => "/api/0.6/changesets", :method => :post },
18         { :controller => "api/changesets", :action => "create" }
19       )
20       assert_routing(
21         { :path => "/api/0.6/changeset/1/upload", :method => :post },
22         { :controller => "api/changesets", :action => "upload", :id => "1" }
23       )
24       assert_routing(
25         { :path => "/api/0.6/changeset/1/download", :method => :get },
26         { :controller => "api/changesets", :action => "download", :id => "1" }
27       )
28       assert_routing(
29         { :path => "/api/0.6/changeset/1", :method => :get },
30         { :controller => "api/changesets", :action => "show", :id => "1" }
31       )
32       assert_routing(
33         { :path => "/api/0.6/changeset/1.json", :method => :get },
34         { :controller => "api/changesets", :action => "show", :id => "1", :format => "json" }
35       )
36       assert_routing(
37         { :path => "/api/0.6/changeset/1/subscribe", :method => :post },
38         { :controller => "api/changesets", :action => "subscribe", :id => "1" }
39       )
40       assert_routing(
41         { :path => "/api/0.6/changeset/1/subscribe.json", :method => :post },
42         { :controller => "api/changesets", :action => "subscribe", :id => "1", :format => "json" }
43       )
44       assert_routing(
45         { :path => "/api/0.6/changeset/1/unsubscribe", :method => :post },
46         { :controller => "api/changesets", :action => "unsubscribe", :id => "1" }
47       )
48       assert_routing(
49         { :path => "/api/0.6/changeset/1/unsubscribe.json", :method => :post },
50         { :controller => "api/changesets", :action => "unsubscribe", :id => "1", :format => "json" }
51       )
52       assert_routing(
53         { :path => "/api/0.6/changeset/1", :method => :put },
54         { :controller => "api/changesets", :action => "update", :id => "1" }
55       )
56       assert_routing(
57         { :path => "/api/0.6/changeset/1/close", :method => :put },
58         { :controller => "api/changesets", :action => "close", :id => "1" }
59       )
60
61       assert_recognizes(
62         { :controller => "api/changesets", :action => "create" },
63         { :path => "/api/0.6/changeset/create", :method => :put }
64       )
65     end
66
67     ##
68     # test the query functionality of changesets
69     def test_index
70       private_user = create(:user, :data_public => false)
71       private_user_changeset = create(:changeset, :user => private_user)
72       private_user_closed_changeset = create(:changeset, :closed, :user => private_user)
73       user = create(:user)
74       changeset = create(:changeset, :user => user)
75       closed_changeset = create(:changeset, :closed, :user => user, :created_at => Time.utc(2008, 1, 1, 0, 0, 0), :closed_at => Time.utc(2008, 1, 2, 0, 0, 0))
76       changeset2 = create(:changeset, :min_lat => (5 * GeoRecord::SCALE).round, :min_lon => (5 * GeoRecord::SCALE).round, :max_lat => (15 * GeoRecord::SCALE).round, :max_lon => (15 * GeoRecord::SCALE).round)
77       changeset3 = create(:changeset, :min_lat => (4.5 * GeoRecord::SCALE).round, :min_lon => (4.5 * GeoRecord::SCALE).round, :max_lat => (5 * GeoRecord::SCALE).round, :max_lon => (5 * GeoRecord::SCALE).round)
78
79       get api_changesets_path(:bbox => "-10,-10, 10, 10")
80       assert_response :success, "can't get changesets in bbox"
81       assert_changesets_in_order [changeset3, changeset2]
82
83       get api_changesets_path(:bbox => "4.5,4.5,4.6,4.6")
84       assert_response :success, "can't get changesets in bbox"
85       assert_changesets_in_order [changeset3]
86
87       # not found when looking for changesets of non-existing users
88       get api_changesets_path(:user => User.maximum(:id) + 1)
89       assert_response :not_found
90       assert_equal "text/plain", @response.media_type
91       get api_changesets_path(:display_name => " ")
92       assert_response :not_found
93       assert_equal "text/plain", @response.media_type
94
95       # can't get changesets of user 1 without authenticating
96       get api_changesets_path(:user => private_user.id)
97       assert_response :not_found, "shouldn't be able to get changesets by non-public user (ID)"
98       get api_changesets_path(:display_name => private_user.display_name)
99       assert_response :not_found, "shouldn't be able to get changesets by non-public user (name)"
100
101       # but this should work
102       auth_header = bearer_authorization_header private_user
103       get api_changesets_path(:user => private_user.id), :headers => auth_header
104       assert_response :success, "can't get changesets by user ID"
105       assert_changesets_in_order [private_user_changeset, private_user_closed_changeset]
106
107       get api_changesets_path(:display_name => private_user.display_name), :headers => auth_header
108       assert_response :success, "can't get changesets by user name"
109       assert_changesets_in_order [private_user_changeset, private_user_closed_changeset]
110
111       # test json endpoint
112       get api_changesets_path(:display_name => private_user.display_name), :headers => auth_header, :params => { :format => "json" }
113       assert_response :success, "can't get changesets by user name"
114
115       js = ActiveSupport::JSON.decode(@response.body)
116       assert_not_nil js
117
118       assert_equal Settings.api_version, js["version"]
119       assert_equal Settings.generator, js["generator"]
120       assert_equal 2, js["changesets"].count
121
122       # check that the correct error is given when we provide both UID and name
123       get api_changesets_path(:user => private_user.id,
124                               :display_name => private_user.display_name), :headers => auth_header
125       assert_response :bad_request, "should be a bad request to have both ID and name specified"
126
127       get api_changesets_path(:user => private_user.id, :open => true), :headers => auth_header
128       assert_response :success, "can't get changesets by user and open"
129       assert_changesets_in_order [private_user_changeset]
130
131       get api_changesets_path(:time => "2007-12-31"), :headers => auth_header
132       assert_response :success, "can't get changesets by time-since"
133       assert_changesets_in_order [changeset3, changeset2, changeset, private_user_changeset, private_user_closed_changeset, closed_changeset]
134
135       get api_changesets_path(:time => "2008-01-01T12:34Z"), :headers => auth_header
136       assert_response :success, "can't get changesets by time-since with hour"
137       assert_changesets_in_order [changeset3, changeset2, changeset, private_user_changeset, private_user_closed_changeset, closed_changeset]
138
139       get api_changesets_path(:time => "2007-12-31T23:59Z,2008-01-02T00:01Z"), :headers => auth_header
140       assert_response :success, "can't get changesets by time-range"
141       assert_changesets_in_order [closed_changeset]
142
143       get api_changesets_path(:open => "true"), :headers => auth_header
144       assert_response :success, "can't get changesets by open-ness"
145       assert_changesets_in_order [changeset3, changeset2, changeset, private_user_changeset]
146
147       get api_changesets_path(:closed => "true"), :headers => auth_header
148       assert_response :success, "can't get changesets by closed-ness"
149       assert_changesets_in_order [private_user_closed_changeset, closed_changeset]
150
151       get api_changesets_path(:closed => "true", :user => private_user.id), :headers => auth_header
152       assert_response :success, "can't get changesets by closed-ness and user"
153       assert_changesets_in_order [private_user_closed_changeset]
154
155       get api_changesets_path(:closed => "true", :user => user.id), :headers => auth_header
156       assert_response :success, "can't get changesets by closed-ness and user"
157       assert_changesets_in_order [closed_changeset]
158
159       get api_changesets_path(:changesets => "#{private_user_changeset.id},#{changeset.id},#{closed_changeset.id}"), :headers => auth_header
160       assert_response :success, "can't get changesets by id (as comma-separated string)"
161       assert_changesets_in_order [changeset, private_user_changeset, closed_changeset]
162
163       get api_changesets_path(:changesets => ""), :headers => auth_header
164       assert_response :bad_request, "should be a bad request since changesets is empty"
165     end
166
167     ##
168     # test the query functionality of changesets with the limit parameter
169     def test_index_limit
170       user = create(:user)
171       changeset1 = create(:changeset, :closed, :user => user, :created_at => Time.utc(2008, 1, 1, 0, 0, 0), :closed_at => Time.utc(2008, 1, 2, 0, 0, 0))
172       changeset2 = create(:changeset, :closed, :user => user, :created_at => Time.utc(2008, 2, 1, 0, 0, 0), :closed_at => Time.utc(2008, 2, 2, 0, 0, 0))
173       changeset3 = create(:changeset, :closed, :user => user, :created_at => Time.utc(2008, 3, 1, 0, 0, 0), :closed_at => Time.utc(2008, 3, 2, 0, 0, 0))
174       changeset4 = create(:changeset, :closed, :user => user, :created_at => Time.utc(2008, 4, 1, 0, 0, 0), :closed_at => Time.utc(2008, 4, 2, 0, 0, 0))
175       changeset5 = create(:changeset, :closed, :user => user, :created_at => Time.utc(2008, 5, 1, 0, 0, 0), :closed_at => Time.utc(2008, 5, 2, 0, 0, 0))
176
177       get api_changesets_path
178       assert_response :success
179       assert_changesets_in_order [changeset5, changeset4, changeset3, changeset2, changeset1]
180
181       get api_changesets_path(:limit => "3")
182       assert_response :success
183       assert_changesets_in_order [changeset5, changeset4, changeset3]
184
185       get api_changesets_path(:limit => "0")
186       assert_response :bad_request
187
188       get api_changesets_path(:limit => Settings.max_changeset_query_limit)
189       assert_response :success
190       assert_changesets_in_order [changeset5, changeset4, changeset3, changeset2, changeset1]
191
192       get api_changesets_path(:limit => Settings.max_changeset_query_limit + 1)
193       assert_response :bad_request
194     end
195
196     ##
197     # test the query functionality of sequential changesets with order and time parameters
198     def test_index_order
199       user = create(:user)
200       changeset1 = create(:changeset, :closed, :user => user, :created_at => Time.utc(2008, 1, 1, 0, 0, 0), :closed_at => Time.utc(2008, 1, 2, 0, 0, 0))
201       changeset2 = create(:changeset, :closed, :user => user, :created_at => Time.utc(2008, 2, 1, 0, 0, 0), :closed_at => Time.utc(2008, 2, 2, 0, 0, 0))
202       changeset3 = create(:changeset, :closed, :user => user, :created_at => Time.utc(2008, 3, 1, 0, 0, 0), :closed_at => Time.utc(2008, 3, 2, 0, 0, 0))
203       changeset4 = create(:changeset, :closed, :user => user, :created_at => Time.utc(2008, 4, 1, 0, 0, 0), :closed_at => Time.utc(2008, 4, 2, 0, 0, 0))
204       changeset5 = create(:changeset, :closed, :user => user, :created_at => Time.utc(2008, 5, 1, 0, 0, 0), :closed_at => Time.utc(2008, 5, 2, 0, 0, 0))
205       changeset6 = create(:changeset, :closed, :user => user, :created_at => Time.utc(2008, 6, 1, 0, 0, 0), :closed_at => Time.utc(2008, 6, 2, 0, 0, 0))
206
207       get api_changesets_path
208       assert_response :success
209       assert_changesets_in_order [changeset6, changeset5, changeset4, changeset3, changeset2, changeset1]
210
211       get api_changesets_path(:order => "oldest")
212       assert_response :success
213       assert_changesets_in_order [changeset1, changeset2, changeset3, changeset4, changeset5, changeset6]
214
215       [
216         # lower time bound at the opening time of a changeset
217         ["2008-02-01T00:00:00Z", "2008-05-15T00:00:00Z", [changeset5, changeset4, changeset3, changeset2], [changeset5, changeset4, changeset3, changeset2]],
218         # lower time bound in the middle of a changeset
219         ["2008-02-01T12:00:00Z", "2008-05-15T00:00:00Z", [changeset5, changeset4, changeset3, changeset2], [changeset5, changeset4, changeset3]],
220         # lower time bound at the closing time of a changeset
221         ["2008-02-02T00:00:00Z", "2008-05-15T00:00:00Z", [changeset5, changeset4, changeset3, changeset2], [changeset5, changeset4, changeset3]],
222         # lower time bound after the closing time of a changeset
223         ["2008-02-02T00:00:01Z", "2008-05-15T00:00:00Z", [changeset5, changeset4, changeset3], [changeset5, changeset4, changeset3]],
224         # upper time bound in the middle of a changeset
225         ["2007-09-09T12:00:00Z", "2008-04-01T12:00:00Z", [changeset4, changeset3, changeset2, changeset1], [changeset4, changeset3, changeset2, changeset1]],
226         # empty range
227         ["2009-02-02T00:00:01Z", "2018-05-15T00:00:00Z", [], []]
228       ].each do |from, to, interval_changesets, point_changesets|
229         get api_changesets_path(:time => "#{from},#{to}")
230         assert_response :success
231         assert_changesets_in_order interval_changesets
232
233         get api_changesets_path(:from => from, :to => to)
234         assert_response :success
235         assert_changesets_in_order point_changesets
236
237         get api_changesets_path(:from => from, :to => to, :order => "oldest")
238         assert_response :success
239         assert_changesets_in_order point_changesets.reverse
240       end
241     end
242
243     ##
244     # test the query functionality of overlapping changesets with order and time parameters
245     def test_index_order_overlapping
246       user = create(:user)
247       changeset1 = create(:changeset, :closed, :user => user, :created_at => Time.utc(2015, 6, 4, 17, 0, 0), :closed_at => Time.utc(2015, 6, 4, 17, 0, 0))
248       changeset2 = create(:changeset, :closed, :user => user, :created_at => Time.utc(2015, 6, 4, 16, 0, 0), :closed_at => Time.utc(2015, 6, 4, 18, 0, 0))
249       changeset3 = create(:changeset, :closed, :user => user, :created_at => Time.utc(2015, 6, 4, 14, 0, 0), :closed_at => Time.utc(2015, 6, 4, 20, 0, 0))
250       changeset4 = create(:changeset, :closed, :user => user, :created_at => Time.utc(2015, 6, 3, 23, 0, 0), :closed_at => Time.utc(2015, 6, 4, 23, 0, 0))
251       create(:changeset, :closed, :user => user, :created_at => Time.utc(2015, 6, 2, 23, 0, 0), :closed_at => Time.utc(2015, 6, 3, 23, 0, 0))
252
253       get api_changesets_path(:time => "2015-06-04T00:00:00Z")
254       assert_response :success
255       assert_changesets_in_order [changeset1, changeset2, changeset3, changeset4]
256
257       get api_changesets_path(:from => "2015-06-04T00:00:00Z")
258       assert_response :success
259       assert_changesets_in_order [changeset1, changeset2, changeset3]
260
261       get api_changesets_path(:from => "2015-06-04T00:00:00Z", :order => "oldest")
262       assert_response :success
263       assert_changesets_in_order [changeset3, changeset2, changeset1]
264
265       get api_changesets_path(:time => "2015-06-04T16:00:00Z,2015-06-04T17:30:00Z")
266       assert_response :success
267       assert_changesets_in_order [changeset1, changeset2, changeset3, changeset4]
268
269       get api_changesets_path(:from => "2015-06-04T16:00:00Z", :to => "2015-06-04T17:30:00Z")
270       assert_response :success
271       assert_changesets_in_order [changeset1, changeset2]
272
273       get api_changesets_path(:from => "2015-06-04T16:00:00Z", :to => "2015-06-04T17:30:00Z", :order => "oldest")
274       assert_response :success
275       assert_changesets_in_order [changeset2, changeset1]
276     end
277
278     ##
279     # check that errors are returned if garbage is inserted
280     # into query strings
281     def test_index_invalid
282       ["abracadabra!",
283        "1,2,3,F",
284        ";drop table users;"].each do |bbox|
285         get api_changesets_path(:bbox => bbox)
286         assert_response :bad_request, "'#{bbox}' isn't a bbox"
287       end
288
289       ["now()",
290        "00-00-00",
291        ";drop table users;",
292        ",",
293        "-,-"].each do |time|
294         get api_changesets_path(:time => time)
295         assert_response :bad_request, "'#{time}' isn't a valid time range"
296       end
297
298       ["me",
299        "foobar",
300        "-1",
301        "0"].each do |uid|
302         get api_changesets_path(:user => uid)
303         assert_response :bad_request, "'#{uid}' isn't a valid user ID"
304       end
305
306       get api_changesets_path(:order => "oldest", :time => "2008-01-01T00:00Z,2018-01-01T00:00Z")
307       assert_response :bad_request, "cannot use order=oldest with time"
308     end
309
310     # -----------------------
311     # Test simple changeset creation
312     # -----------------------
313
314     def test_create
315       auth_header = bearer_authorization_header create(:user, :data_public => false)
316       # Create the first user's changeset
317       xml = "<osm><changeset>" \
318             "<tag k='created_by' v='osm test suite checking changesets'/>" \
319             "</changeset></osm>"
320       post api_changesets_path, :params => xml, :headers => auth_header
321       assert_require_public_data
322
323       auth_header = bearer_authorization_header
324       # Create the first user's changeset
325       xml = "<osm><changeset>" \
326             "<tag k='created_by' v='osm test suite checking changesets'/>" \
327             "</changeset></osm>"
328       post api_changesets_path, :params => xml, :headers => auth_header
329
330       assert_response :success, "Creation of changeset did not return success status"
331       newid = @response.body.to_i
332
333       # check end time, should be an hour ahead of creation time
334       cs = Changeset.find(newid)
335       duration = cs.closed_at - cs.created_at
336       # the difference can either be a rational, or a floating point number
337       # of seconds, depending on the code path taken :-(
338       if duration.instance_of?(Rational)
339         assert_equal Rational(1, 24), duration, "initial idle timeout should be an hour (#{cs.created_at} -> #{cs.closed_at})"
340       else
341         # must be number of seconds...
342         assert_equal 3600, duration.round, "initial idle timeout should be an hour (#{cs.created_at} -> #{cs.closed_at})"
343       end
344
345       # checks if uploader was subscribed
346       assert_equal 1, cs.subscribers.length
347     end
348
349     def test_create_invalid
350       auth_header = bearer_authorization_header create(:user, :data_public => false)
351       xml = "<osm><changeset></osm>"
352       post api_changesets_path, :params => xml, :headers => auth_header
353       assert_require_public_data
354
355       ## Try the public user
356       auth_header = bearer_authorization_header
357       xml = "<osm><changeset></osm>"
358       post api_changesets_path, :params => xml, :headers => auth_header
359       assert_response :bad_request, "creating a invalid changeset should fail"
360     end
361
362     def test_create_invalid_no_content
363       ## First check with no auth
364       post api_changesets_path
365       assert_response :unauthorized, "shouldn't be able to create a changeset with no auth"
366
367       ## Now try to with a non-public user
368       auth_header = bearer_authorization_header create(:user, :data_public => false)
369       post api_changesets_path, :headers => auth_header
370       assert_require_public_data
371
372       ## Try an inactive user
373       auth_header = bearer_authorization_header create(:user, :pending)
374       post api_changesets_path, :headers => auth_header
375       assert_inactive_user
376
377       ## Now try to use a normal user
378       auth_header = bearer_authorization_header
379       post api_changesets_path, :headers => auth_header
380       assert_response :bad_request, "creating a changeset with no content should fail"
381     end
382
383     def test_create_wrong_method
384       auth_header = bearer_authorization_header
385
386       put api_changesets_path, :headers => auth_header
387       assert_response :not_found
388       assert_template "rescues/routing_error"
389     end
390
391     def test_create_legacy_path
392       auth_header = bearer_authorization_header
393       xml = "<osm><changeset></changeset></osm>"
394
395       assert_difference "Changeset.count", 1 do
396         put "/api/0.6/changeset/create", :params => xml, :headers => auth_header
397       end
398
399       assert_response :success, "Creation of changeset did not return success status"
400       assert_equal Changeset.last.id, @response.body.to_i
401     end
402
403     ##
404     # check that the changeset can be shown and returns the correct
405     # document structure.
406     def test_show
407       changeset = create(:changeset)
408
409       get changeset_show_path(changeset)
410       assert_response :success, "cannot get first changeset"
411
412       assert_dom "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
413       assert_single_changeset changeset do
414         assert_dom "> discussion", 0
415       end
416
417       get changeset_show_path(changeset), :params => { :include_discussion => true }
418       assert_response :success, "cannot get first changeset with comments"
419
420       assert_dom "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
421       assert_single_changeset changeset do
422         assert_dom "> discussion", 1
423         assert_dom "> discussion > comment", 0
424       end
425     end
426
427     def test_show_comments
428       # all comments visible
429       changeset = create(:changeset, :closed)
430       comment1, comment2, comment3 = create_list(:changeset_comment, 3, :changeset_id => changeset.id)
431
432       get changeset_show_path(changeset), :params => { :include_discussion => true }
433       assert_response :success, "cannot get closed changeset with comments"
434
435       assert_dom "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1 do
436         assert_single_changeset changeset do
437           assert_dom "> discussion", 1 do
438             assert_dom "> comment", 3 do |dom_comments|
439               assert_dom dom_comments[0], "> @id", comment1.id.to_s
440               assert_dom dom_comments[0], "> @visible", "true"
441               assert_dom dom_comments[1], "> @id", comment2.id.to_s
442               assert_dom dom_comments[1], "> @visible", "true"
443               assert_dom dom_comments[2], "> @id", comment3.id.to_s
444               assert_dom dom_comments[2], "> @visible", "true"
445             end
446           end
447         end
448       end
449
450       # one hidden comment not included because not asked for
451       comment2.update(:visible => false)
452       changeset.reload
453
454       get changeset_show_path(changeset), :params => { :include_discussion => true }
455       assert_response :success, "cannot get closed changeset with comments"
456
457       assert_dom "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
458       assert_single_changeset changeset do
459         assert_dom "> discussion", 1 do
460           assert_dom "> comment", 2 do |dom_comments|
461             assert_dom dom_comments[0], "> @id", comment1.id.to_s
462             assert_dom dom_comments[0], "> @visible", "true"
463             assert_dom dom_comments[1], "> @id", comment3.id.to_s
464             assert_dom dom_comments[1], "> @visible", "true"
465           end
466         end
467       end
468
469       # one hidden comment not included because no permissions
470       get changeset_show_path(changeset), :params => { :include_discussion => true, :show_hidden_comments => true }
471       assert_response :success, "cannot get closed changeset with comments"
472
473       assert_dom "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
474       assert_single_changeset changeset do
475         assert_dom "> discussion", 1 do
476           assert_dom "> comment", 2 do |dom_comments|
477             assert_dom dom_comments[0], "> @id", comment1.id.to_s
478             assert_dom dom_comments[0], "> @visible", "true"
479             # maybe will show an empty comment element with visible=false in the future
480             assert_dom dom_comments[1], "> @id", comment3.id.to_s
481             assert_dom dom_comments[1], "> @visible", "true"
482           end
483         end
484       end
485
486       # one hidden comment shown to moderators
487       moderator_user = create(:moderator_user)
488       auth_header = bearer_authorization_header moderator_user
489       get changeset_show_path(changeset), :params => { :include_discussion => true, :show_hidden_comments => true },
490                                           :headers => auth_header
491       assert_response :success, "cannot get closed changeset with comments"
492
493       assert_dom "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
494       assert_single_changeset changeset do
495         assert_dom "> discussion", 1 do
496           assert_dom "> comment", 3 do |dom_comments|
497             assert_dom dom_comments[0], "> @id", comment1.id.to_s
498             assert_dom dom_comments[0], "> @visible", "true"
499             assert_dom dom_comments[1], "> @id", comment2.id.to_s
500             assert_dom dom_comments[1], "> @visible", "false"
501             assert_dom dom_comments[2], "> @id", comment3.id.to_s
502             assert_dom dom_comments[2], "> @visible", "true"
503           end
504         end
505       end
506     end
507
508     def test_show_tags
509       changeset = create(:changeset, :closed)
510       create(:changeset_tag, :changeset => changeset, :k => "created_by", :v => "JOSM/1.5 (18364)")
511       create(:changeset_tag, :changeset => changeset, :k => "comment", :v => "changeset comment")
512
513       get changeset_show_path(changeset)
514
515       assert_response :success
516       assert_dom "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
517       assert_single_changeset changeset do
518         assert_dom "> tag", 2
519         assert_dom "> tag[k='created_by'][v='JOSM/1.5 (18364)']", 1
520         assert_dom "> tag[k='comment'][v='changeset comment']", 1
521       end
522     end
523
524     def test_show_json
525       changeset = create(:changeset)
526
527       get changeset_show_path(changeset), :params => { :format => "json" }
528       assert_response :success, "cannot get first changeset"
529
530       js = ActiveSupport::JSON.decode(@response.body)
531       assert_not_nil js
532
533       assert_equal Settings.api_version, js["version"]
534       assert_equal Settings.generator, js["generator"]
535       assert_single_changeset_json changeset, js
536       assert_nil js["changeset"]["tags"]
537       assert_nil js["changeset"]["comments"]
538       assert_equal changeset.user.id, js["changeset"]["uid"]
539       assert_equal changeset.user.display_name, js["changeset"]["user"]
540
541       get changeset_show_path(changeset), :params => { :format => "json", :include_discussion => true }
542       assert_response :success, "cannot get first changeset with comments"
543
544       js = ActiveSupport::JSON.decode(@response.body)
545       assert_not_nil js
546       assert_equal Settings.api_version, js["version"]
547       assert_equal Settings.generator, js["generator"]
548       assert_single_changeset_json changeset, js
549       assert_nil js["changeset"]["tags"]
550       assert_nil js["changeset"]["min_lat"]
551       assert_nil js["changeset"]["min_lon"]
552       assert_nil js["changeset"]["max_lat"]
553       assert_nil js["changeset"]["max_lon"]
554       assert_equal 0, js["changeset"]["comments"].count
555     end
556
557     def test_show_comments_json
558       # all comments visible
559       changeset = create(:changeset, :closed)
560       comment0, comment1, comment2 = create_list(:changeset_comment, 3, :changeset_id => changeset.id)
561
562       get changeset_show_path(changeset), :params => { :format => "json", :include_discussion => true }
563       assert_response :success, "cannot get closed changeset with comments"
564
565       js = ActiveSupport::JSON.decode(@response.body)
566       assert_not_nil js
567       assert_equal Settings.api_version, js["version"]
568       assert_equal Settings.generator, js["generator"]
569       assert_single_changeset_json changeset, js
570       assert_equal 3, js["changeset"]["comments"].count
571       assert_equal comment0.id, js["changeset"]["comments"][0]["id"]
572       assert js["changeset"]["comments"][0]["visible"]
573       assert_equal comment1.id, js["changeset"]["comments"][1]["id"]
574       assert js["changeset"]["comments"][1]["visible"]
575       assert_equal comment2.id, js["changeset"]["comments"][2]["id"]
576       assert js["changeset"]["comments"][2]["visible"]
577
578       # one hidden comment not included because not asked for
579       comment1.update(:visible => false)
580       changeset.reload
581
582       get changeset_show_path(changeset), :params => { :format => "json", :include_discussion => true }
583       assert_response :success, "cannot get closed changeset with comments"
584
585       js = ActiveSupport::JSON.decode(@response.body)
586       assert_not_nil js
587       assert_equal Settings.api_version, js["version"]
588       assert_equal Settings.generator, js["generator"]
589       assert_single_changeset_json changeset, js
590       assert_equal 2, js["changeset"]["comments"].count
591       assert_equal comment0.id, js["changeset"]["comments"][0]["id"]
592       assert js["changeset"]["comments"][0]["visible"]
593       assert_equal comment2.id, js["changeset"]["comments"][1]["id"]
594       assert js["changeset"]["comments"][1]["visible"]
595
596       # one hidden comment not included because no permissions
597       get changeset_show_path(changeset), :params => { :format => "json", :include_discussion => true, :show_hidden_comments => true }
598       assert_response :success, "cannot get closed changeset with comments"
599
600       js = ActiveSupport::JSON.decode(@response.body)
601       assert_not_nil js
602       assert_equal Settings.api_version, js["version"]
603       assert_equal Settings.generator, js["generator"]
604       assert_single_changeset_json changeset, js
605       assert_equal 2, js["changeset"]["comments"].count
606       assert_equal comment0.id, js["changeset"]["comments"][0]["id"]
607       assert js["changeset"]["comments"][0]["visible"]
608       # maybe will show an empty comment element with visible=false in the future
609       assert_equal comment2.id, js["changeset"]["comments"][1]["id"]
610       assert js["changeset"]["comments"][1]["visible"]
611
612       # one hidden comment shown to moderators
613       moderator_user = create(:moderator_user)
614       auth_header = bearer_authorization_header moderator_user
615       get changeset_show_path(changeset), :params => { :format => "json", :include_discussion => true, :show_hidden_comments => true },
616                                           :headers => auth_header
617       assert_response :success, "cannot get closed changeset with comments"
618
619       js = ActiveSupport::JSON.decode(@response.body)
620       assert_not_nil js
621       assert_equal Settings.api_version, js["version"]
622       assert_equal Settings.generator, js["generator"]
623       assert_single_changeset_json changeset, js
624       assert_equal 3, js["changeset"]["comments"].count
625       assert_equal comment0.id, js["changeset"]["comments"][0]["id"]
626       assert js["changeset"]["comments"][0]["visible"]
627       assert_equal comment1.id, js["changeset"]["comments"][1]["id"]
628       assert_not js["changeset"]["comments"][1]["visible"]
629       assert_equal comment2.id, js["changeset"]["comments"][2]["id"]
630       assert js["changeset"]["comments"][2]["visible"]
631     end
632
633     def test_show_tags_json
634       changeset = create(:changeset, :closed)
635       create(:changeset_tag, :changeset => changeset, :k => "created_by", :v => "JOSM/1.5 (18364)")
636       create(:changeset_tag, :changeset => changeset, :k => "comment", :v => "changeset comment")
637
638       get changeset_show_path(changeset, :format => "json")
639
640       assert_response :success
641       js = ActiveSupport::JSON.decode(@response.body)
642       assert_not_nil js
643       assert_equal Settings.api_version, js["version"]
644       assert_equal Settings.generator, js["generator"]
645       assert_single_changeset_json changeset, js
646       assert_equal 2, js["changeset"]["tags"].count
647       assert_equal "JOSM/1.5 (18364)", js["changeset"]["tags"]["created_by"]
648       assert_equal "changeset comment", js["changeset"]["tags"]["comment"]
649     end
650
651     def test_show_bbox_json
652       # test bbox attribute
653       changeset = create(:changeset, :min_lat => (-5 * GeoRecord::SCALE).round, :min_lon => (5 * GeoRecord::SCALE).round,
654                                      :max_lat => (15 * GeoRecord::SCALE).round, :max_lon => (12 * GeoRecord::SCALE).round)
655
656       get changeset_show_path(changeset), :params => { :format => "json" }
657       assert_response :success, "cannot get first changeset"
658
659       js = ActiveSupport::JSON.decode(@response.body)
660       assert_not_nil js
661       assert_equal(-5, js["changeset"]["min_lat"])
662       assert_equal 5, js["changeset"]["min_lon"]
663       assert_equal 15, js["changeset"]["max_lat"]
664       assert_equal 12, js["changeset"]["max_lon"]
665     end
666
667     ##
668     # check that a changeset that doesn't exist returns an appropriate message
669     def test_show_not_found
670       [0, -32, 233455644, "afg", "213"].each do |id|
671         get changeset_show_path(id)
672         assert_response :not_found, "should get a not found"
673       rescue ActionController::UrlGenerationError => e
674         assert_match(/No route matches/, e.to_s)
675       end
676     end
677
678     ##
679     # test that the user who opened a change can close it
680     def test_close
681       private_user = create(:user, :data_public => false)
682       private_changeset = create(:changeset, :user => private_user)
683       user = create(:user)
684       changeset = create(:changeset, :user => user)
685
686       ## Try without authentication
687       put changeset_close_path(changeset)
688       assert_response :unauthorized
689
690       ## Try using the non-public user
691       auth_header = bearer_authorization_header private_user
692       put changeset_close_path(private_changeset), :headers => auth_header
693       assert_require_public_data
694
695       ## The try with the public user
696       auth_header = bearer_authorization_header user
697
698       cs_id = changeset.id
699       put changeset_close_path(cs_id), :headers => auth_header
700       assert_response :success
701
702       # test that it really is closed now
703       cs = Changeset.find(changeset.id)
704       assert_not(cs.open?,
705                  "changeset should be closed now (#{cs.closed_at} > #{Time.now.utc}.")
706     end
707
708     ##
709     # test that a different user can't close another user's changeset
710     def test_close_invalid
711       user = create(:user)
712       changeset = create(:changeset)
713
714       auth_header = bearer_authorization_header user
715
716       put changeset_close_path(changeset), :headers => auth_header
717       assert_response :conflict
718       assert_equal "The user doesn't own that changeset", @response.body
719     end
720
721     ##
722     # test that you can't close using another method
723     def test_close_method_invalid
724       user = create(:user)
725       changeset = create(:changeset, :user => user)
726
727       auth_header = bearer_authorization_header user
728
729       get changeset_close_path(changeset), :headers => auth_header
730       assert_response :not_found
731       assert_template "rescues/routing_error"
732
733       post changeset_close_path(changeset), :headers => auth_header
734       assert_response :not_found
735       assert_template "rescues/routing_error"
736     end
737
738     ##
739     # check that you can't close a changeset that isn't found
740     def test_close_not_found
741       cs_ids = [0, -132, "123"]
742
743       # First try to do it with no auth
744       cs_ids.each do |id|
745         put changeset_close_path(id)
746         assert_response :unauthorized, "Shouldn't be able close the non-existant changeset #{id}, when not authorized"
747       rescue ActionController::UrlGenerationError => e
748         assert_match(/No route matches/, e.to_s)
749       end
750
751       # Now try with auth
752       auth_header = bearer_authorization_header
753       cs_ids.each do |id|
754         put changeset_close_path(id), :headers => auth_header
755         assert_response :not_found, "The changeset #{id} doesn't exist, so can't be closed"
756       rescue ActionController::UrlGenerationError => e
757         assert_match(/No route matches/, e.to_s)
758       end
759     end
760
761     ##
762     # upload something simple, but valid and check that it can
763     # be read back ok
764     # Also try without auth and another user.
765     def test_upload_simple_valid
766       private_user = create(:user, :data_public => false)
767       private_changeset = create(:changeset, :user => private_user)
768       user = create(:user)
769       changeset = create(:changeset, :user => user)
770
771       node = create(:node)
772       way = create(:way)
773       relation = create(:relation)
774       other_relation = create(:relation)
775       # create some tags, since we test that they are removed later
776       create(:node_tag, :node => node)
777       create(:way_tag, :way => way)
778       create(:relation_tag, :relation => relation)
779
780       ## Try with no auth
781       changeset_id = changeset.id
782
783       # simple diff to change a node, way and relation by removing
784       # their tags
785       diff = <<~CHANGESET
786         <osmChange>
787          <modify>
788           <node id='#{node.id}' lon='0' lat='0' changeset='#{changeset_id}' version='1'/>
789           <way id='#{way.id}' changeset='#{changeset_id}' version='1'>
790            <nd ref='#{node.id}'/>
791           </way>
792          </modify>
793          <modify>
794           <relation id='#{relation.id}' changeset='#{changeset_id}' version='1'>
795            <member type='way' role='some' ref='#{way.id}'/>
796            <member type='node' role='some' ref='#{node.id}'/>
797            <member type='relation' role='some' ref='#{other_relation.id}'/>
798           </relation>
799          </modify>
800         </osmChange>
801       CHANGESET
802
803       # upload it
804       post changeset_upload_path(changeset), :params => diff
805       assert_response :unauthorized,
806                       "shouldn't be able to upload a simple valid diff to changeset: #{@response.body}"
807
808       ## Now try with a private user
809       auth_header = bearer_authorization_header private_user
810       changeset_id = private_changeset.id
811
812       # simple diff to change a node, way and relation by removing
813       # their tags
814       diff = <<~CHANGESET
815         <osmChange>
816          <modify>
817           <node id='#{node.id}' lon='0' lat='0' changeset='#{changeset_id}' version='1'/>
818           <way id='#{way.id}' changeset='#{changeset_id}' version='1'>
819            <nd ref='#{node.id}'/>
820           </way>
821          </modify>
822          <modify>
823           <relation id='#{relation.id}' changeset='#{changeset_id}' version='1'>
824            <member type='way' role='some' ref='#{way.id}'/>
825            <member type='node' role='some' ref='#{node.id}'/>
826            <member type='relation' role='some' ref='#{other_relation.id}'/>
827           </relation>
828          </modify>
829         </osmChange>
830       CHANGESET
831
832       # upload it
833       post changeset_upload_path(private_changeset), :params => diff, :headers => auth_header
834       assert_response :forbidden,
835                       "can't upload a simple valid diff to changeset: #{@response.body}"
836
837       ## Now try with the public user
838       auth_header = bearer_authorization_header user
839       changeset_id = changeset.id
840
841       # simple diff to change a node, way and relation by removing
842       # their tags
843       diff = <<~CHANGESET
844         <osmChange>
845          <modify>
846           <node id='#{node.id}' lon='0' lat='0' changeset='#{changeset_id}' version='1'/>
847           <way id='#{way.id}' changeset='#{changeset_id}' version='1'>
848            <nd ref='#{node.id}'/>
849           </way>
850          </modify>
851          <modify>
852           <relation id='#{relation.id}' changeset='#{changeset_id}' version='1'>
853            <member type='way' role='some' ref='#{way.id}'/>
854            <member type='node' role='some' ref='#{node.id}'/>
855            <member type='relation' role='some' ref='#{other_relation.id}'/>
856           </relation>
857          </modify>
858         </osmChange>
859       CHANGESET
860
861       # upload it
862       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
863       assert_response :success,
864                       "can't upload a simple valid diff to changeset: #{@response.body}"
865
866       # check that the changes made it into the database
867       assert_equal 0, Node.find(node.id).tags.size, "node #{node.id} should now have no tags"
868       assert_equal 0, Way.find(way.id).tags.size, "way #{way.id} should now have no tags"
869       assert_equal 0, Relation.find(relation.id).tags.size, "relation #{relation.id} should now have no tags"
870     end
871
872     ##
873     # upload something which creates new objects using placeholders
874     def test_upload_create_valid
875       user = create(:user)
876       changeset = create(:changeset, :user => user)
877       node = create(:node)
878       way = create(:way_with_nodes, :nodes_count => 2)
879       relation = create(:relation)
880
881       auth_header = bearer_authorization_header user
882
883       # simple diff to create a node way and relation using placeholders
884       diff = <<~CHANGESET
885         <osmChange>
886          <create>
887           <node id='-1' lon='0' lat='0' changeset='#{changeset.id}'>
888            <tag k='foo' v='bar'/>
889            <tag k='baz' v='bat'/>
890           </node>
891           <way id='-1' changeset='#{changeset.id}'>
892            <nd ref='#{node.id}'/>
893           </way>
894          </create>
895          <create>
896           <relation id='-1' changeset='#{changeset.id}'>
897            <member type='way' role='some' ref='#{way.id}'/>
898            <member type='node' role='some' ref='#{node.id}'/>
899            <member type='relation' role='some' ref='#{relation.id}'/>
900           </relation>
901          </create>
902         </osmChange>
903       CHANGESET
904
905       # upload it
906       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
907       assert_response :success,
908                       "can't upload a simple valid creation to changeset: #{@response.body}"
909
910       # check the returned payload
911       new_node_id, new_way_id, new_rel_id = nil
912       assert_dom "diffResult[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1 do
913         # inspect the response to find out what the new element IDs are
914         # check the old IDs are all present and negative one
915         # check the versions are present and equal one
916         assert_dom "> node", 1 do |(node_el)|
917           new_node_id = node_el["new_id"].to_i
918           assert_dom "> @old_id", "-1"
919           assert_dom "> @new_version", "1"
920         end
921         assert_dom "> way", 1 do |(way_el)|
922           new_way_id = way_el["new_id"].to_i
923           assert_dom "> @old_id", "-1"
924           assert_dom "> @new_version", "1"
925         end
926         assert_dom "> relation", 1 do |(rel_el)|
927           new_rel_id = rel_el["new_id"].to_i
928           assert_dom "> @old_id", "-1"
929           assert_dom "> @new_version", "1"
930         end
931       end
932
933       # check that the changes made it into the database
934       assert_equal 2, Node.find(new_node_id).tags.size, "new node should have two tags"
935       assert_equal 0, Way.find(new_way_id).tags.size, "new way should have no tags"
936       assert_equal 0, Relation.find(new_rel_id).tags.size, "new relation should have no tags"
937     end
938
939     ##
940     # test a complex delete where we delete elements which rely on eachother
941     # in the same transaction.
942     def test_upload_delete
943       changeset = create(:changeset)
944       super_relation = create(:relation)
945       used_relation = create(:relation)
946       used_way = create(:way)
947       used_node = create(:node)
948       create(:relation_member, :relation => super_relation, :member => used_relation)
949       create(:relation_member, :relation => super_relation, :member => used_way)
950       create(:relation_member, :relation => super_relation, :member => used_node)
951
952       auth_header = bearer_authorization_header changeset.user
953
954       diff = XML::Document.new
955       diff.root = XML::Node.new "osmChange"
956       delete = XML::Node.new "delete"
957       diff.root << delete
958       delete << xml_node_for_relation(super_relation)
959       delete << xml_node_for_relation(used_relation)
960       delete << xml_node_for_way(used_way)
961       delete << xml_node_for_node(used_node)
962
963       # update the changeset to one that this user owns
964       %w[node way relation].each do |type|
965         delete.find("//osmChange/delete/#{type}").each do |n|
966           n["changeset"] = changeset.id.to_s
967         end
968       end
969
970       # upload it
971       post changeset_upload_path(changeset), :params => diff.to_s, :headers => auth_header
972       assert_response :success,
973                       "can't upload a deletion diff to changeset: #{@response.body}"
974
975       # check the response is well-formed
976       assert_select "diffResult>node", 1
977       assert_select "diffResult>way", 1
978       assert_select "diffResult>relation", 2
979
980       # check that everything was deleted
981       assert_not Node.find(used_node.id).visible
982       assert_not Way.find(used_way.id).visible
983       assert_not Relation.find(super_relation.id).visible
984       assert_not Relation.find(used_relation.id).visible
985     end
986
987     ##
988     # test uploading a delete with no lat/lon, as they are optional in
989     # the osmChange spec.
990     def test_upload_nolatlon_delete
991       node = create(:node)
992       changeset = create(:changeset)
993
994       auth_header = bearer_authorization_header changeset.user
995       diff = "<osmChange><delete><node id='#{node.id}' version='#{node.version}' changeset='#{changeset.id}'/></delete></osmChange>"
996
997       # upload it
998       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
999       assert_response :success,
1000                       "can't upload a deletion diff to changeset: #{@response.body}"
1001
1002       # check the response is well-formed
1003       assert_select "diffResult>node", 1
1004
1005       # check that everything was deleted
1006       assert_not Node.find(node.id).visible
1007     end
1008
1009     def test_repeated_changeset_create
1010       3.times do
1011         auth_header = bearer_authorization_header
1012
1013         # create a temporary changeset
1014         xml = "<osm><changeset>" \
1015               "<tag k='created_by' v='osm test suite checking changesets'/>" \
1016               "</changeset></osm>"
1017         assert_difference "Changeset.count", 1 do
1018           post api_changesets_path, :params => xml, :headers => auth_header
1019         end
1020         assert_response :success
1021       end
1022     end
1023
1024     def test_upload_large_changeset
1025       user = create(:user)
1026       auth_header = bearer_authorization_header user
1027
1028       # create an old changeset to ensure we have the maximum rate limit
1029       create(:changeset, :user => user, :created_at => Time.now.utc - 28.days)
1030
1031       # create a changeset
1032       post api_changesets_path, :params => "<osm><changeset/></osm>", :headers => auth_header
1033       assert_response :success, "Should be able to create a changeset: #{@response.body}"
1034       changeset_id = @response.body.to_i
1035
1036       # upload some widely-spaced nodes, spiralling positive and negative
1037       diff = <<~CHANGESET
1038         <osmChange>
1039          <create>
1040           <node id='-1' lon='-20' lat='-10' changeset='#{changeset_id}'/>
1041           <node id='-10' lon='20'  lat='10' changeset='#{changeset_id}'/>
1042           <node id='-2' lon='-40' lat='-20' changeset='#{changeset_id}'/>
1043           <node id='-11' lon='40'  lat='20' changeset='#{changeset_id}'/>
1044           <node id='-3' lon='-60' lat='-30' changeset='#{changeset_id}'/>
1045           <node id='-12' lon='60'  lat='30' changeset='#{changeset_id}'/>
1046           <node id='-4' lon='-80' lat='-40' changeset='#{changeset_id}'/>
1047           <node id='-13' lon='80'  lat='40' changeset='#{changeset_id}'/>
1048           <node id='-5' lon='-100' lat='-50' changeset='#{changeset_id}'/>
1049           <node id='-14' lon='100'  lat='50' changeset='#{changeset_id}'/>
1050           <node id='-6' lon='-120' lat='-60' changeset='#{changeset_id}'/>
1051           <node id='-15' lon='120'  lat='60' changeset='#{changeset_id}'/>
1052           <node id='-7' lon='-140' lat='-70' changeset='#{changeset_id}'/>
1053           <node id='-16' lon='140'  lat='70' changeset='#{changeset_id}'/>
1054           <node id='-8' lon='-160' lat='-80' changeset='#{changeset_id}'/>
1055           <node id='-17' lon='160'  lat='80' changeset='#{changeset_id}'/>
1056           <node id='-9' lon='-179.9' lat='-89.9' changeset='#{changeset_id}'/>
1057           <node id='-18' lon='179.9'  lat='89.9' changeset='#{changeset_id}'/>
1058          </create>
1059         </osmChange>
1060       CHANGESET
1061
1062       # upload it, which used to cause an error like "PGError: ERROR:
1063       # integer out of range" (bug #2152). but shouldn't any more.
1064       post changeset_upload_path(changeset_id), :params => diff, :headers => auth_header
1065       assert_response :success,
1066                       "can't upload a spatially-large diff to changeset: #{@response.body}"
1067
1068       # check that the changeset bbox is within bounds
1069       cs = Changeset.find(changeset_id)
1070       assert_operator cs.min_lon, :>=, -180 * GeoRecord::SCALE, "Minimum longitude (#{cs.min_lon / GeoRecord::SCALE}) should be >= -180 to be valid."
1071       assert_operator cs.max_lon, :<=, 180 * GeoRecord::SCALE, "Maximum longitude (#{cs.max_lon / GeoRecord::SCALE}) should be <= 180 to be valid."
1072       assert_operator cs.min_lat, :>=, -90 * GeoRecord::SCALE, "Minimum latitude (#{cs.min_lat / GeoRecord::SCALE}) should be >= -90 to be valid."
1073       assert_operator cs.max_lat, :<=, 90 * GeoRecord::SCALE, "Maximum latitude (#{cs.max_lat / GeoRecord::SCALE}) should be <= 90 to be valid."
1074     end
1075
1076     ##
1077     # test that deleting stuff in a transaction doesn't bypass the checks
1078     # to ensure that used elements are not deleted.
1079     def test_upload_delete_invalid
1080       changeset = create(:changeset)
1081       relation = create(:relation)
1082       other_relation = create(:relation)
1083       used_way = create(:way)
1084       used_node = create(:node)
1085       create(:relation_member, :relation => relation, :member => used_way)
1086       create(:relation_member, :relation => relation, :member => used_node)
1087
1088       auth_header = bearer_authorization_header changeset.user
1089
1090       diff = XML::Document.new
1091       diff.root = XML::Node.new "osmChange"
1092       delete = XML::Node.new "delete"
1093       diff.root << delete
1094       delete << xml_node_for_relation(other_relation)
1095       delete << xml_node_for_way(used_way)
1096       delete << xml_node_for_node(used_node)
1097
1098       # update the changeset to one that this user owns
1099       %w[node way relation].each do |type|
1100         delete.find("//osmChange/delete/#{type}").each do |n|
1101           n["changeset"] = changeset.id.to_s
1102         end
1103       end
1104
1105       # upload it
1106       post changeset_upload_path(changeset), :params => diff.to_s, :headers => auth_header
1107       assert_response :precondition_failed,
1108                       "shouldn't be able to upload a invalid deletion diff: #{@response.body}"
1109       assert_equal "Precondition failed: Way #{used_way.id} is still used by relations #{relation.id}.", @response.body
1110
1111       # check that nothing was, in fact, deleted
1112       assert Node.find(used_node.id).visible
1113       assert Way.find(used_way.id).visible
1114       assert Relation.find(relation.id).visible
1115       assert Relation.find(other_relation.id).visible
1116     end
1117
1118     ##
1119     # test that a conditional delete of an in use object works.
1120     def test_upload_delete_if_unused
1121       changeset = create(:changeset)
1122       super_relation = create(:relation)
1123       used_relation = create(:relation)
1124       used_way = create(:way)
1125       used_node = create(:node)
1126       create(:relation_member, :relation => super_relation, :member => used_relation)
1127       create(:relation_member, :relation => super_relation, :member => used_way)
1128       create(:relation_member, :relation => super_relation, :member => used_node)
1129
1130       auth_header = bearer_authorization_header changeset.user
1131
1132       diff = XML::Document.new
1133       diff.root = XML::Node.new "osmChange"
1134       delete = XML::Node.new "delete"
1135       diff.root << delete
1136       delete["if-unused"] = ""
1137       delete << xml_node_for_relation(used_relation)
1138       delete << xml_node_for_way(used_way)
1139       delete << xml_node_for_node(used_node)
1140
1141       # update the changeset to one that this user owns
1142       %w[node way relation].each do |type|
1143         delete.find("//osmChange/delete/#{type}").each do |n|
1144           n["changeset"] = changeset.id.to_s
1145         end
1146       end
1147
1148       # upload it
1149       post changeset_upload_path(changeset), :params => diff.to_s, :headers => auth_header
1150       assert_response :success,
1151                       "can't do a conditional delete of in use objects: #{@response.body}"
1152
1153       # check the returned payload
1154       assert_dom "diffResult[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1 do
1155         # check the old IDs are all present and what we expect
1156         # check the new IDs are all present and unchanged
1157         # check the new versions are all present and unchanged
1158         assert_dom "> node", 1 do
1159           assert_dom "> @old_id", used_node.id.to_s
1160           assert_dom "> @new_id", used_node.id.to_s
1161           assert_dom "> @new_version", used_node.version.to_s
1162         end
1163         assert_dom "> way", 1 do
1164           assert_dom "> @old_id", used_way.id.to_s
1165           assert_dom "> @new_id", used_way.id.to_s
1166           assert_dom "> @new_version", used_way.version.to_s
1167         end
1168         assert_dom "> relation", 1 do
1169           assert_dom "> @old_id", used_relation.id.to_s
1170           assert_dom "> @new_id", used_relation.id.to_s
1171           assert_dom "> @new_version", used_relation.version.to_s
1172         end
1173       end
1174
1175       # check that nothing was, in fact, deleted
1176       assert Node.find(used_node.id).visible
1177       assert Way.find(used_way.id).visible
1178       assert Relation.find(used_relation.id).visible
1179     end
1180
1181     ##
1182     # upload an element with a really long tag value
1183     def test_upload_invalid_too_long_tag
1184       changeset = create(:changeset)
1185
1186       auth_header = bearer_authorization_header changeset.user
1187
1188       # simple diff to create a node way and relation using placeholders
1189       diff = <<~CHANGESET
1190         <osmChange>
1191          <create>
1192           <node id='-1' lon='0' lat='0' changeset='#{changeset.id}'>
1193            <tag k='foo' v='#{'x' * 256}'/>
1194           </node>
1195          </create>
1196         </osmChange>
1197       CHANGESET
1198
1199       # upload it
1200       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1201       assert_response :bad_request,
1202                       "shouldn't be able to upload too long a tag to changeset: #{@response.body}"
1203     end
1204
1205     ##
1206     # upload something which creates new objects and inserts them into
1207     # existing containers using placeholders.
1208     def test_upload_complex
1209       way = create(:way)
1210       node = create(:node)
1211       relation = create(:relation)
1212       create(:way_node, :way => way, :node => node)
1213
1214       changeset = create(:changeset)
1215
1216       auth_header = bearer_authorization_header changeset.user
1217
1218       # simple diff to create a node way and relation using placeholders
1219       diff = <<~CHANGESET
1220         <osmChange>
1221          <create>
1222           <node id='-1' lon='0' lat='0' changeset='#{changeset.id}'>
1223            <tag k='foo' v='bar'/>
1224            <tag k='baz' v='bat'/>
1225           </node>
1226          </create>
1227          <modify>
1228           <way id='#{way.id}' changeset='#{changeset.id}' version='1'>
1229            <nd ref='-1'/>
1230            <nd ref='#{node.id}'/>
1231           </way>
1232           <relation id='#{relation.id}' changeset='#{changeset.id}' version='1'>
1233            <member type='way' role='some' ref='#{way.id}'/>
1234            <member type='node' role='some' ref='-1'/>
1235            <member type='relation' role='some' ref='#{relation.id}'/>
1236           </relation>
1237          </modify>
1238         </osmChange>
1239       CHANGESET
1240
1241       # upload it
1242       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1243       assert_response :success,
1244                       "can't upload a complex diff to changeset: #{@response.body}"
1245
1246       # check the returned payload
1247       new_node_id = nil
1248       assert_dom "diffResult[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1 do
1249         assert_dom "> node", 1 do |(node_el)|
1250           new_node_id = node_el["new_id"].to_i
1251         end
1252         assert_dom "> way", 1
1253         assert_dom "> relation", 1
1254       end
1255
1256       # check that the changes made it into the database
1257       assert_equal 2, Node.find(new_node_id).tags.size, "new node should have two tags"
1258       assert_equal [new_node_id, node.id], Way.find(way.id).nds, "way nodes should match"
1259       Relation.find(relation.id).members.each do |type, id, _role|
1260         assert_equal new_node_id, id, "relation should contain new node" if type == "node"
1261       end
1262     end
1263
1264     ##
1265     # create a diff which references several changesets, which should cause
1266     # a rollback and none of the diff gets committed
1267     def test_upload_invalid_changesets
1268       changeset = create(:changeset)
1269       other_changeset = create(:changeset, :user => changeset.user)
1270       node = create(:node)
1271       way = create(:way)
1272       relation = create(:relation)
1273       other_relation = create(:relation)
1274
1275       auth_header = bearer_authorization_header changeset.user
1276
1277       # simple diff to create a node way and relation using placeholders
1278       diff = <<~CHANGESET
1279         <osmChange>
1280          <modify>
1281           <node id='#{node.id}' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
1282           <way id='#{way.id}' changeset='#{changeset.id}' version='1'>
1283            <nd ref='#{node.id}'/>
1284           </way>
1285          </modify>
1286          <modify>
1287           <relation id='#{relation.id}' changeset='#{changeset.id}' version='1'>
1288            <member type='way' role='some' ref='#{way.id}'/>
1289            <member type='node' role='some' ref='#{node.id}'/>
1290            <member type='relation' role='some' ref='#{other_relation.id}'/>
1291           </relation>
1292          </modify>
1293          <create>
1294           <node id='-1' lon='0' lat='0' changeset='#{other_changeset.id}'>
1295            <tag k='foo' v='bar'/>
1296            <tag k='baz' v='bat'/>
1297           </node>
1298          </create>
1299         </osmChange>
1300       CHANGESET
1301
1302       # upload it
1303       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1304       assert_response :conflict,
1305                       "uploading a diff with multiple changesets should have failed"
1306
1307       # check that objects are unmodified
1308       assert_nodes_are_equal(node, Node.find(node.id))
1309       assert_ways_are_equal(way, Way.find(way.id))
1310       assert_relations_are_equal(relation, Relation.find(relation.id))
1311     end
1312
1313     ##
1314     # upload multiple versions of the same element in the same diff.
1315     def test_upload_multiple_valid
1316       node = create(:node)
1317       changeset = create(:changeset)
1318       auth_header = bearer_authorization_header changeset.user
1319
1320       # change the location of a node multiple times, each time referencing
1321       # the last version. doesn't this depend on version numbers being
1322       # sequential?
1323       diff = <<~CHANGESET
1324         <osmChange>
1325          <modify>
1326           <node id='#{node.id}' lon='0.0' lat='0.0' changeset='#{changeset.id}' version='1'/>
1327           <node id='#{node.id}' lon='0.1' lat='0.0' changeset='#{changeset.id}' version='2'/>
1328           <node id='#{node.id}' lon='0.1' lat='0.1' changeset='#{changeset.id}' version='3'/>
1329           <node id='#{node.id}' lon='0.1' lat='0.2' changeset='#{changeset.id}' version='4'/>
1330           <node id='#{node.id}' lon='0.2' lat='0.2' changeset='#{changeset.id}' version='5'/>
1331           <node id='#{node.id}' lon='0.3' lat='0.2' changeset='#{changeset.id}' version='6'/>
1332           <node id='#{node.id}' lon='0.3' lat='0.3' changeset='#{changeset.id}' version='7'/>
1333           <node id='#{node.id}' lon='0.9' lat='0.9' changeset='#{changeset.id}' version='8'/>
1334          </modify>
1335         </osmChange>
1336       CHANGESET
1337
1338       # upload it
1339       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1340       assert_response :success,
1341                       "can't upload multiple versions of an element in a diff: #{@response.body}"
1342
1343       # check the response is well-formed. its counter-intuitive, but the
1344       # API will return multiple elements with the same ID and different
1345       # version numbers for each change we made.
1346       assert_select "diffResult>node", 8
1347     end
1348
1349     ##
1350     # upload multiple versions of the same element in the same diff, but
1351     # keep the version numbers the same.
1352     def test_upload_multiple_duplicate
1353       node = create(:node)
1354       changeset = create(:changeset)
1355
1356       auth_header = bearer_authorization_header changeset.user
1357
1358       diff = <<~CHANGESET
1359         <osmChange>
1360          <modify>
1361           <node id='#{node.id}' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
1362           <node id='#{node.id}' lon='1' lat='1' changeset='#{changeset.id}' version='1'/>
1363          </modify>
1364         </osmChange>
1365       CHANGESET
1366
1367       # upload it
1368       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1369       assert_response :conflict,
1370                       "shouldn't be able to upload the same element twice in a diff: #{@response.body}"
1371     end
1372
1373     ##
1374     # try to upload some elements without specifying the version
1375     def test_upload_missing_version
1376       changeset = create(:changeset)
1377
1378       auth_header = bearer_authorization_header changeset.user
1379
1380       diff = <<~CHANGESET
1381         <osmChange>
1382          <modify>
1383          <node id='1' lon='1' lat='1' changeset='#{changeset.id}'/>
1384          </modify>
1385         </osmChange>
1386       CHANGESET
1387
1388       # upload it
1389       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1390       assert_response :bad_request,
1391                       "shouldn't be able to upload an element without version: #{@response.body}"
1392     end
1393
1394     ##
1395     # try to upload with commands other than create, modify, or delete
1396     def test_action_upload_invalid
1397       changeset = create(:changeset)
1398
1399       auth_header = bearer_authorization_header changeset.user
1400
1401       diff = <<~CHANGESET
1402         <osmChange>
1403           <ping>
1404            <node id='1' lon='1' lat='1' changeset='#{changeset.id}' />
1405           </ping>
1406         </osmChange>
1407       CHANGESET
1408       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1409       assert_response :bad_request, "Shouldn't be able to upload a diff with the action ping"
1410       assert_equal("Unknown action ping, choices are create, modify, delete", @response.body)
1411     end
1412
1413     ##
1414     # upload a valid changeset which has a mixture of whitespace
1415     # to check a bug reported by ivansanchez (#1565).
1416     def test_upload_whitespace_valid
1417       changeset = create(:changeset)
1418       node = create(:node)
1419       way = create(:way_with_nodes, :nodes_count => 2)
1420       relation = create(:relation)
1421       other_relation = create(:relation)
1422       create(:relation_tag, :relation => relation)
1423
1424       auth_header = bearer_authorization_header changeset.user
1425
1426       diff = <<~CHANGESET
1427         <osmChange>
1428          <modify><node id='#{node.id}' lon='0' lat='0' changeset='#{changeset.id}'
1429           version='1'></node>
1430           <node id='#{node.id}' lon='1' lat='1' changeset='#{changeset.id}' version='2'><tag k='k' v='v'/></node></modify>
1431          <modify>
1432          <relation id='#{relation.id}' changeset='#{changeset.id}' version='1'><member
1433            type='way' role='some' ref='#{way.id}'/><member
1434             type='node' role='some' ref='#{node.id}'/>
1435            <member type='relation' role='some' ref='#{other_relation.id}'/>
1436           </relation>
1437          </modify></osmChange>
1438       CHANGESET
1439
1440       # upload it
1441       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1442       assert_response :success,
1443                       "can't upload a valid diff with whitespace variations to changeset: #{@response.body}"
1444
1445       # check the response is well-formed
1446       assert_select "diffResult>node", 2
1447       assert_select "diffResult>relation", 1
1448
1449       # check that the changes made it into the database
1450       assert_equal 1, Node.find(node.id).tags.size, "node #{node.id} should now have one tag"
1451       assert_equal 0, Relation.find(relation.id).tags.size, "relation #{relation.id} should now have no tags"
1452     end
1453
1454     ##
1455     # test that a placeholder can be reused within the same upload.
1456     def test_upload_reuse_placeholder_valid
1457       changeset = create(:changeset)
1458
1459       auth_header = bearer_authorization_header changeset.user
1460
1461       diff = <<~CHANGESET
1462         <osmChange>
1463          <create>
1464           <node id='-1' lon='0' lat='0' changeset='#{changeset.id}'>
1465            <tag k="foo" v="bar"/>
1466           </node>
1467          </create>
1468          <modify>
1469           <node id='-1' lon='1' lat='1' changeset='#{changeset.id}' version='1'/>
1470          </modify>
1471          <delete>
1472           <node id='-1' lon='2' lat='2' changeset='#{changeset.id}' version='2'/>
1473          </delete>
1474         </osmChange>
1475       CHANGESET
1476
1477       # upload it
1478       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1479       assert_response :success,
1480                       "can't upload a valid diff with re-used placeholders to changeset: #{@response.body}"
1481
1482       # check the response is well-formed
1483       assert_select "diffResult>node", 3
1484       assert_select "diffResult>node[old_id='-1']", 3
1485     end
1486
1487     ##
1488     # test what happens if a diff upload re-uses placeholder IDs in an
1489     # illegal way.
1490     def test_upload_placeholder_invalid
1491       changeset = create(:changeset)
1492
1493       auth_header = bearer_authorization_header changeset.user
1494
1495       diff = <<~CHANGESET
1496         <osmChange>
1497          <create>
1498           <node id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
1499           <node id='-1' lon='1' lat='1' changeset='#{changeset.id}' version='1'/>
1500           <node id='-1' lon='2' lat='2' changeset='#{changeset.id}' version='2'/>
1501          </create>
1502         </osmChange>
1503       CHANGESET
1504
1505       # upload it
1506       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1507       assert_response :bad_request,
1508                       "shouldn't be able to re-use placeholder IDs"
1509
1510       # placeholder_ids must be unique across all action blocks
1511       diff = <<~CHANGESET
1512         <osmChange>
1513          <create>
1514           <node id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
1515          </create>
1516          <create>
1517           <node id='-1' lon='1' lat='1' changeset='#{changeset.id}' version='1'/>
1518          </create>
1519         </osmChange>
1520       CHANGESET
1521
1522       # upload it
1523       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1524       assert_response :bad_request,
1525                       "shouldn't be able to re-use placeholder IDs"
1526     end
1527
1528     def test_upload_process_order
1529       changeset = create(:changeset)
1530
1531       auth_header = bearer_authorization_header changeset.user
1532
1533       diff = <<~CHANGESET
1534         <osmChange>
1535         <create>
1536           <node id="-1" lat="1" lon="2" changeset="#{changeset.id}"/>
1537           <way id="-1" changeset="#{changeset.id}">
1538               <nd ref="-1"/>
1539               <nd ref="-2"/>
1540           </way>
1541           <node id="-2" lat="1" lon="2" changeset="#{changeset.id}"/>
1542         </create>
1543         </osmChange>
1544       CHANGESET
1545
1546       # upload it
1547       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1548       assert_response :bad_request,
1549                       "shouldn't refer elements behind it"
1550     end
1551
1552     def test_upload_duplicate_delete
1553       changeset = create(:changeset)
1554
1555       auth_header = bearer_authorization_header changeset.user
1556
1557       diff = <<~CHANGESET
1558         <osmChange>
1559           <create>
1560             <node id="-1" lat="39" lon="116" changeset="#{changeset.id}" />
1561           </create>
1562           <delete>
1563             <node id="-1" version="1" changeset="#{changeset.id}" />
1564             <node id="-1" version="1" changeset="#{changeset.id}" />
1565           </delete>
1566         </osmChange>
1567       CHANGESET
1568
1569       # upload it
1570       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1571       assert_response :gone,
1572                       "transaction should be cancelled by second deletion"
1573
1574       diff = <<~CHANGESET
1575         <osmChange>
1576           <create>
1577             <node id="-1" lat="39" lon="116" changeset="#{changeset.id}" />
1578           </create>
1579           <delete if-unused="true">
1580             <node id="-1" version="1" changeset="#{changeset.id}" />
1581             <node id="-1" version="1" changeset="#{changeset.id}" />
1582           </delete>
1583         </osmChange>
1584       CHANGESET
1585
1586       # upload it
1587       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1588
1589       assert_select "diffResult>node", 3
1590       assert_select "diffResult>node[old_id='-1']", 3
1591       assert_select "diffResult>node[new_version='1']", 1
1592       assert_select "diffResult>node[new_version='2']", 1
1593     end
1594
1595     ##
1596     # test that uploading a way referencing invalid placeholders gives a
1597     # proper error, not a 500.
1598     def test_upload_placeholder_invalid_way
1599       changeset = create(:changeset)
1600       way = create(:way)
1601
1602       auth_header = bearer_authorization_header changeset.user
1603
1604       diff = <<~CHANGESET
1605         <osmChange>
1606          <create>
1607           <node id="-1" lon="0.0" lat="0.0" changeset="#{changeset.id}" version="1"/>
1608           <node id="-2" lon="0.1" lat="0.1" changeset="#{changeset.id}" version="1"/>
1609           <node id="-3" lon="0.2" lat="0.2" changeset="#{changeset.id}" version="1"/>
1610           <way id="-1" changeset="#{changeset.id}" version="1">
1611            <nd ref="-1"/>
1612            <nd ref="-2"/>
1613            <nd ref="-3"/>
1614            <nd ref="-4"/>
1615           </way>
1616          </create>
1617         </osmChange>
1618       CHANGESET
1619
1620       # upload it
1621       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1622       assert_response :bad_request,
1623                       "shouldn't be able to use invalid placeholder IDs"
1624       assert_equal "Placeholder node not found for reference -4 in way -1", @response.body
1625
1626       # the same again, but this time use an existing way
1627       diff = <<~CHANGESET
1628         <osmChange>
1629          <create>
1630           <node id="-1" lon="0.0" lat="0.0" changeset="#{changeset.id}" version="1"/>
1631           <node id="-2" lon="0.1" lat="0.1" changeset="#{changeset.id}" version="1"/>
1632           <node id="-3" lon="0.2" lat="0.2" changeset="#{changeset.id}" version="1"/>
1633           <way id="#{way.id}" changeset="#{changeset.id}" version="1">
1634            <nd ref="-1"/>
1635            <nd ref="-2"/>
1636            <nd ref="-3"/>
1637            <nd ref="-4"/>
1638           </way>
1639          </create>
1640         </osmChange>
1641       CHANGESET
1642
1643       # upload it
1644       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1645       assert_response :bad_request,
1646                       "shouldn't be able to use invalid placeholder IDs"
1647       assert_equal "Placeholder node not found for reference -4 in way #{way.id}", @response.body
1648     end
1649
1650     ##
1651     # test that uploading a relation referencing invalid placeholders gives a
1652     # proper error, not a 500.
1653     def test_upload_placeholder_invalid_relation
1654       changeset = create(:changeset)
1655       relation = create(:relation)
1656
1657       auth_header = bearer_authorization_header changeset.user
1658
1659       diff = <<~CHANGESET
1660         <osmChange>
1661          <create>
1662           <node id="-1" lon="0.0" lat="0.0" changeset="#{changeset.id}" version="1"/>
1663           <node id="-2" lon="0.1" lat="0.1" changeset="#{changeset.id}" version="1"/>
1664           <node id="-3" lon="0.2" lat="0.2" changeset="#{changeset.id}" version="1"/>
1665           <relation id="-1" changeset="#{changeset.id}" version="1">
1666            <member type="node" role="foo" ref="-1"/>
1667            <member type="node" role="foo" ref="-2"/>
1668            <member type="node" role="foo" ref="-3"/>
1669            <member type="node" role="foo" ref="-4"/>
1670           </relation>
1671          </create>
1672         </osmChange>
1673       CHANGESET
1674
1675       # upload it
1676       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1677       assert_response :bad_request,
1678                       "shouldn't be able to use invalid placeholder IDs"
1679       assert_equal "Placeholder Node not found for reference -4 in relation -1.", @response.body
1680
1681       # the same again, but this time use an existing relation
1682       diff = <<~CHANGESET
1683         <osmChange>
1684          <create>
1685           <node id="-1" lon="0.0" lat="0.0" changeset="#{changeset.id}" version="1"/>
1686           <node id="-2" lon="0.1" lat="0.1" changeset="#{changeset.id}" version="1"/>
1687           <node id="-3" lon="0.2" lat="0.2" changeset="#{changeset.id}" version="1"/>
1688           <relation id="#{relation.id}" changeset="#{changeset.id}" version="1">
1689            <member type="node" role="foo" ref="-1"/>
1690            <member type="node" role="foo" ref="-2"/>
1691            <member type="node" role="foo" ref="-3"/>
1692            <member type="way" role="bar" ref="-1"/>
1693           </relation>
1694          </create>
1695         </osmChange>
1696       CHANGESET
1697
1698       # upload it
1699       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1700       assert_response :bad_request,
1701                       "shouldn't be able to use invalid placeholder IDs"
1702       assert_equal "Placeholder Way not found for reference -1 in relation #{relation.id}.", @response.body
1703     end
1704
1705     ##
1706     # test what happens if a diff is uploaded containing only a node
1707     # move.
1708     def test_upload_node_move
1709       auth_header = bearer_authorization_header
1710
1711       xml = "<osm><changeset>" \
1712             "<tag k='created_by' v='osm test suite checking changesets'/>" \
1713             "</changeset></osm>"
1714       post api_changesets_path, :params => xml, :headers => auth_header
1715       assert_response :success
1716       changeset_id = @response.body.to_i
1717
1718       old_node = create(:node, :lat => 1, :lon => 1)
1719
1720       diff = XML::Document.new
1721       diff.root = XML::Node.new "osmChange"
1722       modify = XML::Node.new "modify"
1723       xml_old_node = xml_node_for_node(old_node)
1724       xml_old_node["lat"] = 2.0.to_s
1725       xml_old_node["lon"] = 2.0.to_s
1726       xml_old_node["changeset"] = changeset_id.to_s
1727       modify << xml_old_node
1728       diff.root << modify
1729
1730       # upload it
1731       post changeset_upload_path(changeset_id), :params => diff.to_s, :headers => auth_header
1732       assert_response :success,
1733                       "diff should have uploaded OK"
1734
1735       # check the bbox
1736       changeset = Changeset.find(changeset_id)
1737       assert_equal 1 * GeoRecord::SCALE, changeset.min_lon, "min_lon should be 1 degree"
1738       assert_equal 2 * GeoRecord::SCALE, changeset.max_lon, "max_lon should be 2 degrees"
1739       assert_equal 1 * GeoRecord::SCALE, changeset.min_lat, "min_lat should be 1 degree"
1740       assert_equal 2 * GeoRecord::SCALE, changeset.max_lat, "max_lat should be 2 degrees"
1741     end
1742
1743     ##
1744     # test what happens if a diff is uploaded adding a node to a way.
1745     def test_upload_way_extend
1746       auth_header = bearer_authorization_header
1747
1748       xml = "<osm><changeset>" \
1749             "<tag k='created_by' v='osm test suite checking changesets'/>" \
1750             "</changeset></osm>"
1751       post api_changesets_path, :params => xml, :headers => auth_header
1752       assert_response :success
1753       changeset_id = @response.body.to_i
1754
1755       old_way = create(:way)
1756       create(:way_node, :way => old_way, :node => create(:node, :lat => 0.1, :lon => 0.1))
1757
1758       diff = XML::Document.new
1759       diff.root = XML::Node.new "osmChange"
1760       modify = XML::Node.new "modify"
1761       xml_old_way = xml_node_for_way(old_way)
1762       nd_ref = XML::Node.new "nd"
1763       nd_ref["ref"] = create(:node, :lat => 0.3, :lon => 0.3).id.to_s
1764       xml_old_way << nd_ref
1765       xml_old_way["changeset"] = changeset_id.to_s
1766       modify << xml_old_way
1767       diff.root << modify
1768
1769       # upload it
1770       post changeset_upload_path(changeset_id), :params => diff.to_s, :headers => auth_header
1771       assert_response :success,
1772                       "diff should have uploaded OK"
1773
1774       # check the bbox
1775       changeset = Changeset.find(changeset_id)
1776       assert_equal 0.1 * GeoRecord::SCALE, changeset.min_lon, "min_lon should be 0.1 degree"
1777       assert_equal 0.3 * GeoRecord::SCALE, changeset.max_lon, "max_lon should be 0.3 degrees"
1778       assert_equal 0.1 * GeoRecord::SCALE, changeset.min_lat, "min_lat should be 0.1 degree"
1779       assert_equal 0.3 * GeoRecord::SCALE, changeset.max_lat, "max_lat should be 0.3 degrees"
1780     end
1781
1782     ##
1783     # test for more issues in #1568
1784     def test_upload_empty_invalid
1785       changeset = create(:changeset)
1786
1787       auth_header = bearer_authorization_header changeset.user
1788
1789       ["<osmChange/>",
1790        "<osmChange></osmChange>",
1791        "<osmChange><modify/></osmChange>",
1792        "<osmChange><modify></modify></osmChange>"].each do |diff|
1793         # upload it
1794         post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1795         assert_response(:success, "should be able to upload " \
1796                                   "empty changeset: " + diff)
1797       end
1798     end
1799
1800     ##
1801     # test that the X-Error-Format header works to request XML errors
1802     def test_upload_xml_errors
1803       changeset = create(:changeset)
1804       node = create(:node)
1805       create(:relation_member, :member => node)
1806
1807       auth_header = bearer_authorization_header changeset.user
1808
1809       # try and delete a node that is in use
1810       diff = XML::Document.new
1811       diff.root = XML::Node.new "osmChange"
1812       delete = XML::Node.new "delete"
1813       diff.root << delete
1814       delete << xml_node_for_node(node)
1815
1816       # upload it
1817       error_header = error_format_header "xml"
1818       post changeset_upload_path(changeset), :params => diff.to_s, :headers => auth_header.merge(error_header)
1819       assert_response :success,
1820                       "failed to return error in XML format"
1821
1822       # check the returned payload
1823       assert_select "osmError[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
1824       assert_select "osmError>status", 1
1825       assert_select "osmError>message", 1
1826     end
1827
1828     def test_upload_not_found
1829       changeset = create(:changeset)
1830
1831       auth_header = bearer_authorization_header changeset.user
1832
1833       # modify node
1834       diff = <<~CHANGESET
1835         <osmChange>
1836         <modify>
1837           <node id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
1838         </modify>
1839         </osmChange>
1840       CHANGESET
1841
1842       # upload it
1843       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1844       assert_response :not_found, "Node should not be found"
1845
1846       # modify way
1847       diff = <<~CHANGESET
1848         <osmChange>
1849         <modify>
1850           <way id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
1851         </modify>
1852         </osmChange>
1853       CHANGESET
1854
1855       # upload it
1856       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1857       assert_response :not_found, "Way should not be found"
1858
1859       # modify relation
1860       diff = <<~CHANGESET
1861         <osmChange>
1862         <modify>
1863           <relation id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
1864         </modify>
1865         </osmChange>
1866       CHANGESET
1867
1868       # upload it
1869       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1870       assert_response :not_found, "Relation should not be found"
1871
1872       # delete node
1873       diff = <<~CHANGESET
1874         <osmChange>
1875         <delete>
1876           <node id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
1877         </delete>
1878         </osmChange>
1879       CHANGESET
1880
1881       # upload it
1882       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1883       assert_response :not_found, "Node should not be deleted"
1884
1885       # delete way
1886       diff = <<~CHANGESET
1887         <osmChange>
1888         <delete>
1889           <way id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
1890         </delete>
1891         </osmChange>
1892       CHANGESET
1893
1894       # upload it
1895       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1896       assert_response :not_found, "Way should not be deleted"
1897
1898       # delete relation
1899       diff = <<~CHANGESET
1900         <osmChange>
1901         <delete>
1902           <relation id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
1903         </delete>
1904         </osmChange>
1905       CHANGESET
1906
1907       # upload it
1908       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1909       assert_response :not_found, "Relation should not be deleted"
1910     end
1911
1912     def test_upload_relation_placeholder_not_fix
1913       changeset = create(:changeset)
1914
1915       auth_header = bearer_authorization_header changeset.user
1916
1917       # modify node
1918       diff = <<~CHANGESET
1919         <osmChange version='0.6'>
1920           <create>
1921             <relation id='-2' version='0' changeset='#{changeset.id}'>
1922               <member type='relation' role='' ref='-4' />
1923               <tag k='type' v='route' />
1924               <tag k='name' v='AtoB' />
1925             </relation>
1926             <relation id='-3' version='0' changeset='#{changeset.id}'>
1927               <tag k='type' v='route' />
1928               <tag k='name' v='BtoA' />
1929             </relation>
1930             <relation id='-4' version='0' changeset='#{changeset.id}'>
1931               <member type='relation' role='' ref='-2' />
1932               <member type='relation' role='' ref='-3' />
1933               <tag k='type' v='route_master' />
1934               <tag k='name' v='master' />
1935             </relation>
1936           </create>
1937         </osmChange>
1938       CHANGESET
1939
1940       # upload it
1941       post changeset_upload_path(changeset), :params => diff.to_s, :headers => auth_header
1942       assert_response :bad_request, "shouldn't be able to use reference -4 in relation -2: #{@response.body}"
1943       assert_equal "Placeholder Relation not found for reference -4 in relation -2.", @response.body
1944     end
1945
1946     def test_upload_multiple_delete_block
1947       changeset = create(:changeset)
1948
1949       auth_header = bearer_authorization_header changeset.user
1950
1951       node = create(:node)
1952       way = create(:way)
1953       create(:way_node, :way => way, :node => node)
1954       alone_node = create(:node)
1955
1956       # modify node
1957       diff = <<~CHANGESET
1958         <osmChange version='0.6'>
1959           <delete version="0.6">
1960             <node id="#{node.id}" version="#{node.version}" changeset="#{changeset.id}"/>
1961           </delete>
1962           <delete version="0.6" if-unused="true">
1963             <node id="#{alone_node.id}" version="#{alone_node.version}" changeset="#{changeset.id}"/>
1964           </delete>
1965         </osmChange>
1966       CHANGESET
1967
1968       # upload it
1969       post changeset_upload_path(changeset), :params => diff.to_s, :headers => auth_header
1970       assert_response :precondition_failed,
1971                       "shouldn't be able to upload a invalid deletion diff: #{@response.body}"
1972       assert_equal "Precondition failed: Node #{node.id} is still used by ways #{way.id}.", @response.body
1973     end
1974
1975     ##
1976     # test initial rate limit
1977     def test_upload_initial_rate_limit
1978       # create a user
1979       user = create(:user)
1980
1981       # create some objects to use
1982       node = create(:node)
1983       way = create(:way_with_nodes, :nodes_count => 2)
1984       relation = create(:relation)
1985
1986       # create a changeset that puts us near the initial rate limit
1987       changeset = create(:changeset, :user => user,
1988                                      :created_at => Time.now.utc - 5.minutes,
1989                                      :num_changes => Settings.initial_changes_per_hour - 2)
1990
1991       # create authentication header
1992       auth_header = bearer_authorization_header user
1993
1994       # simple diff to create a node way and relation using placeholders
1995       diff = <<~CHANGESET
1996         <osmChange>
1997          <create>
1998           <node id='-1' lon='0' lat='0' changeset='#{changeset.id}'>
1999            <tag k='foo' v='bar'/>
2000            <tag k='baz' v='bat'/>
2001           </node>
2002           <way id='-1' changeset='#{changeset.id}'>
2003            <nd ref='#{node.id}'/>
2004           </way>
2005          </create>
2006          <create>
2007           <relation id='-1' changeset='#{changeset.id}'>
2008            <member type='way' role='some' ref='#{way.id}'/>
2009            <member type='node' role='some' ref='#{node.id}'/>
2010            <member type='relation' role='some' ref='#{relation.id}'/>
2011           </relation>
2012          </create>
2013         </osmChange>
2014       CHANGESET
2015
2016       # upload it
2017       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
2018       assert_response :too_many_requests, "upload did not hit rate limit"
2019     end
2020
2021     ##
2022     # test maximum rate limit
2023     def test_upload_maximum_rate_limit
2024       # create a user
2025       user = create(:user)
2026
2027       # create some objects to use
2028       node = create(:node)
2029       way = create(:way_with_nodes, :nodes_count => 2)
2030       relation = create(:relation)
2031
2032       # create a changeset to establish our initial edit time
2033       changeset = create(:changeset, :user => user,
2034                                      :created_at => Time.now.utc - 28.days)
2035
2036       # create changeset to put us near the maximum rate limit
2037       total_changes = Settings.max_changes_per_hour - 2
2038       while total_changes.positive?
2039         changes = [total_changes, Changeset::MAX_ELEMENTS].min
2040         changeset = create(:changeset, :user => user,
2041                                        :created_at => Time.now.utc - 5.minutes,
2042                                        :num_changes => changes)
2043         total_changes -= changes
2044       end
2045
2046       # create authentication header
2047       auth_header = bearer_authorization_header user
2048
2049       # simple diff to create a node way and relation using placeholders
2050       diff = <<~CHANGESET
2051         <osmChange>
2052          <create>
2053           <node id='-1' lon='0' lat='0' changeset='#{changeset.id}'>
2054            <tag k='foo' v='bar'/>
2055            <tag k='baz' v='bat'/>
2056           </node>
2057           <way id='-1' changeset='#{changeset.id}'>
2058            <nd ref='#{node.id}'/>
2059           </way>
2060          </create>
2061          <create>
2062           <relation id='-1' changeset='#{changeset.id}'>
2063            <member type='way' role='some' ref='#{way.id}'/>
2064            <member type='node' role='some' ref='#{node.id}'/>
2065            <member type='relation' role='some' ref='#{relation.id}'/>
2066           </relation>
2067          </create>
2068         </osmChange>
2069       CHANGESET
2070
2071       # upload it
2072       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
2073       assert_response :too_many_requests, "upload did not hit rate limit"
2074     end
2075
2076     ##
2077     # test initial size limit
2078     def test_upload_initial_size_limit
2079       # create a user
2080       user = create(:user)
2081
2082       # create a changeset that puts us near the initial size limit
2083       changeset = create(:changeset, :user => user,
2084                                      :min_lat => (-0.5 * GeoRecord::SCALE).round, :min_lon => (0.5 * GeoRecord::SCALE).round,
2085                                      :max_lat => (0.5 * GeoRecord::SCALE).round, :max_lon => (2.5 * GeoRecord::SCALE).round)
2086
2087       # create authentication header
2088       auth_header = bearer_authorization_header user
2089
2090       # simple diff to create a node
2091       diff = <<~CHANGESET
2092         <osmChange>
2093          <create>
2094           <node id='-1' lon='0.9' lat='2.9' changeset='#{changeset.id}'>
2095            <tag k='foo' v='bar'/>
2096            <tag k='baz' v='bat'/>
2097           </node>
2098          </create>
2099         </osmChange>
2100       CHANGESET
2101
2102       # upload it
2103       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
2104       assert_response :payload_too_large, "upload did not hit size limit"
2105     end
2106
2107     ##
2108     # test size limit after one week
2109     def test_upload_week_size_limit
2110       # create a user
2111       user = create(:user)
2112
2113       # create a changeset to establish our initial edit time
2114       create(:changeset, :user => user, :created_at => Time.now.utc - 7.days)
2115
2116       # create a changeset that puts us near the initial size limit
2117       changeset = create(:changeset, :user => user,
2118                                      :min_lat => (-0.5 * GeoRecord::SCALE).round, :min_lon => (0.5 * GeoRecord::SCALE).round,
2119                                      :max_lat => (0.5 * GeoRecord::SCALE).round, :max_lon => (2.5 * GeoRecord::SCALE).round)
2120
2121       # create authentication header
2122       auth_header = bearer_authorization_header user
2123
2124       # simple diff to create a node way and relation using placeholders
2125       diff = <<~CHANGESET
2126         <osmChange>
2127          <create>
2128           <node id='-1' lon='35' lat='35' changeset='#{changeset.id}'>
2129            <tag k='foo' v='bar'/>
2130            <tag k='baz' v='bat'/>
2131           </node>
2132          </create>
2133         </osmChange>
2134       CHANGESET
2135
2136       # upload it
2137       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
2138       assert_response :payload_too_large, "upload did not hit size limit"
2139     end
2140
2141     ##
2142     # when we make some simple changes we get the same changes back from the
2143     # diff download.
2144     def test_diff_download_simple
2145       node = create(:node)
2146
2147       ## First try with a non-public user, which should get a forbidden
2148       auth_header = bearer_authorization_header create(:user, :data_public => false)
2149
2150       # create a temporary changeset
2151       xml = "<osm><changeset>" \
2152             "<tag k='created_by' v='osm test suite checking changesets'/>" \
2153             "</changeset></osm>"
2154       post api_changesets_path, :params => xml, :headers => auth_header
2155       assert_response :forbidden
2156
2157       ## Now try with a normal user
2158       auth_header = bearer_authorization_header
2159
2160       # create a temporary changeset
2161       xml = "<osm><changeset>" \
2162             "<tag k='created_by' v='osm test suite checking changesets'/>" \
2163             "</changeset></osm>"
2164       post api_changesets_path, :params => xml, :headers => auth_header
2165       assert_response :success
2166       changeset_id = @response.body.to_i
2167
2168       # add a diff to it
2169       diff = <<~CHANGESET
2170         <osmChange>
2171          <modify>
2172           <node id='#{node.id}' lon='0.0' lat='0.0' changeset='#{changeset_id}' version='1'/>
2173           <node id='#{node.id}' lon='0.1' lat='0.0' changeset='#{changeset_id}' version='2'/>
2174           <node id='#{node.id}' lon='0.1' lat='0.1' changeset='#{changeset_id}' version='3'/>
2175           <node id='#{node.id}' lon='0.1' lat='0.2' changeset='#{changeset_id}' version='4'/>
2176           <node id='#{node.id}' lon='0.2' lat='0.2' changeset='#{changeset_id}' version='5'/>
2177           <node id='#{node.id}' lon='0.3' lat='0.2' changeset='#{changeset_id}' version='6'/>
2178           <node id='#{node.id}' lon='0.3' lat='0.3' changeset='#{changeset_id}' version='7'/>
2179           <node id='#{node.id}' lon='0.9' lat='0.9' changeset='#{changeset_id}' version='8'/>
2180          </modify>
2181         </osmChange>
2182       CHANGESET
2183
2184       # upload it
2185       post changeset_upload_path(changeset_id), :params => diff, :headers => auth_header
2186       assert_response :success,
2187                       "can't upload multiple versions of an element in a diff: #{@response.body}"
2188
2189       get changeset_download_path(changeset_id)
2190       assert_response :success
2191
2192       assert_select "osmChange", 1
2193       assert_select "osmChange>modify", 8
2194       assert_select "osmChange>modify>node", 8
2195     end
2196
2197     ##
2198     # culled this from josm to ensure that nothing in the way that josm
2199     # is formatting the request is causing it to fail.
2200     #
2201     # NOTE: the error turned out to be something else completely!
2202     def test_josm_upload
2203       auth_header = bearer_authorization_header
2204
2205       # create a temporary changeset
2206       xml = "<osm><changeset>" \
2207             "<tag k='created_by' v='osm test suite checking changesets'/>" \
2208             "</changeset></osm>"
2209       post api_changesets_path, :params => xml, :headers => auth_header
2210       assert_response :success
2211       changeset_id = @response.body.to_i
2212
2213       diff = <<~OSMFILE
2214         <osmChange version="0.6" generator="JOSM">
2215         <create version="0.6" generator="JOSM">
2216           <node id='-1' visible='true' changeset='#{changeset_id}' lat='51.49619982187321' lon='-0.18722061869438314' />
2217           <node id='-2' visible='true' changeset='#{changeset_id}' lat='51.496359883909605' lon='-0.18653093576241928' />
2218           <node id='-3' visible='true' changeset='#{changeset_id}' lat='51.49598132358285' lon='-0.18719613290981638' />
2219           <node id='-4' visible='true' changeset='#{changeset_id}' lat='51.4961591711078' lon='-0.18629015888084607' />
2220           <node id='-5' visible='true' changeset='#{changeset_id}' lat='51.49582126021711' lon='-0.18708186591517145' />
2221           <node id='-6' visible='true' changeset='#{changeset_id}' lat='51.49591018437858' lon='-0.1861432441734455' />
2222           <node id='-7' visible='true' changeset='#{changeset_id}' lat='51.49560784152179' lon='-0.18694719410005425' />
2223           <node id='-8' visible='true' changeset='#{changeset_id}' lat='51.49567389979617' lon='-0.1860289771788006' />
2224           <node id='-9' visible='true' changeset='#{changeset_id}' lat='51.49543761398892' lon='-0.186820684213126' />
2225           <way id='-10' action='modify' visible='true' changeset='#{changeset_id}'>
2226             <nd ref='-1' />
2227             <nd ref='-2' />
2228             <nd ref='-3' />
2229             <nd ref='-4' />
2230             <nd ref='-5' />
2231             <nd ref='-6' />
2232             <nd ref='-7' />
2233             <nd ref='-8' />
2234             <nd ref='-9' />
2235             <tag k='highway' v='residential' />
2236             <tag k='name' v='Foobar Street' />
2237           </way>
2238         </create>
2239         </osmChange>
2240       OSMFILE
2241
2242       # upload it
2243       post changeset_upload_path(changeset_id), :params => diff, :headers => auth_header
2244       assert_response :success,
2245                       "can't upload a diff from JOSM: #{@response.body}"
2246
2247       get changeset_download_path(changeset_id)
2248       assert_response :success
2249
2250       assert_select "osmChange", 1
2251       assert_select "osmChange>create>node", 9
2252       assert_select "osmChange>create>way", 1
2253       assert_select "osmChange>create>way>nd", 9
2254       assert_select "osmChange>create>way>tag", 2
2255     end
2256
2257     ##
2258     # when we make some complex changes we get the same changes back from the
2259     # diff download.
2260     def test_diff_download_complex
2261       node = create(:node)
2262       node2 = create(:node)
2263       way = create(:way)
2264       auth_header = bearer_authorization_header
2265
2266       # create a temporary changeset
2267       xml = "<osm><changeset>" \
2268             "<tag k='created_by' v='osm test suite checking changesets'/>" \
2269             "</changeset></osm>"
2270       post api_changesets_path, :params => xml, :headers => auth_header
2271       assert_response :success
2272       changeset_id = @response.body.to_i
2273
2274       # add a diff to it
2275       diff = <<~CHANGESET
2276         <osmChange>
2277          <delete>
2278           <node id='#{node.id}' lon='0.0' lat='0.0' changeset='#{changeset_id}' version='1'/>
2279          </delete>
2280          <create>
2281           <node id='-1' lon='0.9' lat='0.9' changeset='#{changeset_id}' version='0'/>
2282           <node id='-2' lon='0.8' lat='0.9' changeset='#{changeset_id}' version='0'/>
2283           <node id='-3' lon='0.7' lat='0.9' changeset='#{changeset_id}' version='0'/>
2284          </create>
2285          <modify>
2286           <node id='#{node2.id}' lon='2.0' lat='1.5' changeset='#{changeset_id}' version='1'/>
2287           <way id='#{way.id}' changeset='#{changeset_id}' version='1'>
2288            <nd ref='#{node2.id}'/>
2289            <nd ref='-1'/>
2290            <nd ref='-2'/>
2291            <nd ref='-3'/>
2292           </way>
2293          </modify>
2294         </osmChange>
2295       CHANGESET
2296
2297       # upload it
2298       post changeset_upload_path(changeset_id), :params => diff, :headers => auth_header
2299       assert_response :success,
2300                       "can't upload multiple versions of an element in a diff: #{@response.body}"
2301
2302       get changeset_download_path(changeset_id)
2303       assert_response :success
2304
2305       assert_select "osmChange", 1
2306       assert_select "osmChange>create", 3
2307       assert_select "osmChange>delete", 1
2308       assert_select "osmChange>modify", 2
2309       assert_select "osmChange>create>node", 3
2310       assert_select "osmChange>delete>node", 1
2311       assert_select "osmChange>modify>node", 1
2312       assert_select "osmChange>modify>way", 1
2313     end
2314
2315     def test_changeset_download
2316       changeset = create(:changeset)
2317       node = create(:node, :with_history, :version => 1, :changeset => changeset)
2318       tag = create(:old_node_tag, :old_node => node.old_nodes.find_by(:version => 1))
2319       node2 = create(:node, :with_history, :version => 1, :changeset => changeset)
2320       _node3 = create(:node, :with_history, :deleted, :version => 1, :changeset => changeset)
2321       _relation = create(:relation, :with_history, :version => 1, :changeset => changeset)
2322       _relation2 = create(:relation, :with_history, :deleted, :version => 1, :changeset => changeset)
2323
2324       get changeset_download_path(changeset)
2325
2326       assert_response :success
2327
2328       # FIXME: needs more assert_select tests
2329       assert_select "osmChange[version='#{Settings.api_version}'][generator='#{Settings.generator}']" do
2330         assert_select "create", :count => 5
2331         assert_select "create>node[id='#{node.id}'][visible='#{node.visible?}'][version='#{node.version}']" do
2332           assert_select "tag[k='#{tag.k}'][v='#{tag.v}']"
2333         end
2334         assert_select "create>node[id='#{node2.id}']"
2335       end
2336     end
2337
2338     test "sorts downloaded elements by timestamp" do
2339       changeset = create(:changeset)
2340       node1 = create(:old_node, :version => 2, :timestamp => "2020-02-01", :changeset => changeset)
2341       node0 = create(:old_node, :version => 2, :timestamp => "2020-01-01", :changeset => changeset)
2342
2343       get changeset_download_path(changeset)
2344       assert_response :success
2345       assert_dom "modify", :count => 2 do |modify|
2346         assert_dom modify[0], ">node", :count => 1 do |node|
2347           assert_dom node, ">@id", node0.node_id.to_s
2348         end
2349         assert_dom modify[1], ">node", :count => 1 do |node|
2350           assert_dom node, ">@id", node1.node_id.to_s
2351         end
2352       end
2353     end
2354
2355     test "sorts downloaded elements by version" do
2356       changeset = create(:changeset)
2357       node1 = create(:old_node, :version => 3, :timestamp => "2020-01-01", :changeset => changeset)
2358       node0 = create(:old_node, :version => 2, :timestamp => "2020-01-01", :changeset => changeset)
2359
2360       get changeset_download_path(changeset)
2361       assert_response :success
2362       assert_dom "modify", :count => 2 do |modify|
2363         assert_dom modify[0], ">node", :count => 1 do |node|
2364           assert_dom node, ">@id", node0.node_id.to_s
2365         end
2366         assert_dom modify[1], ">node", :count => 1 do |node|
2367           assert_dom node, ">@id", node1.node_id.to_s
2368         end
2369       end
2370     end
2371
2372     ##
2373     # check that the bounding box of a changeset gets updated correctly
2374     # FIXME: This should really be moded to a integration test due to the with_controller
2375     def test_changeset_bbox
2376       way = create(:way)
2377       create(:way_node, :way => way, :node => create(:node, :lat => 0.3, :lon => 0.3))
2378
2379       auth_header = bearer_authorization_header
2380
2381       # create a new changeset
2382       xml = "<osm><changeset/></osm>"
2383       post api_changesets_path, :params => xml, :headers => auth_header
2384       assert_response :success, "Creating of changeset failed."
2385       changeset_id = @response.body.to_i
2386
2387       # add a single node to it
2388       with_controller(NodesController.new) do
2389         xml = "<osm><node lon='0.1' lat='0.2' changeset='#{changeset_id}'/></osm>"
2390         post api_nodes_path, :params => xml, :headers => auth_header
2391         assert_response :success, "Couldn't create node."
2392       end
2393
2394       # get the bounding box back from the changeset
2395       get changeset_show_path(changeset_id)
2396       assert_response :success, "Couldn't read back changeset."
2397       assert_select "osm>changeset[min_lon='0.1000000']", 1
2398       assert_select "osm>changeset[max_lon='0.1000000']", 1
2399       assert_select "osm>changeset[min_lat='0.2000000']", 1
2400       assert_select "osm>changeset[max_lat='0.2000000']", 1
2401
2402       # add another node to it
2403       with_controller(NodesController.new) do
2404         xml = "<osm><node lon='0.2' lat='0.1' changeset='#{changeset_id}'/></osm>"
2405         post api_nodes_path, :params => xml, :headers => auth_header
2406         assert_response :success, "Couldn't create second node."
2407       end
2408
2409       # get the bounding box back from the changeset
2410       get changeset_show_path(changeset_id)
2411       assert_response :success, "Couldn't read back changeset for the second time."
2412       assert_select "osm>changeset[min_lon='0.1000000']", 1
2413       assert_select "osm>changeset[max_lon='0.2000000']", 1
2414       assert_select "osm>changeset[min_lat='0.1000000']", 1
2415       assert_select "osm>changeset[max_lat='0.2000000']", 1
2416
2417       # add (delete) a way to it, which contains a point at (3,3)
2418       with_controller(WaysController.new) do
2419         xml = update_changeset(xml_for_way(way), changeset_id)
2420         delete api_way_path(way), :params => xml.to_s, :headers => auth_header
2421         assert_response :success, "Couldn't delete a way."
2422       end
2423
2424       # get the bounding box back from the changeset
2425       get changeset_show_path(changeset_id)
2426       assert_response :success, "Couldn't read back changeset for the third time."
2427       assert_select "osm>changeset[min_lon='0.1000000']", 1
2428       assert_select "osm>changeset[max_lon='0.3000000']", 1
2429       assert_select "osm>changeset[min_lat='0.1000000']", 1
2430       assert_select "osm>changeset[max_lat='0.3000000']", 1
2431     end
2432
2433     ##
2434     # check updating tags on a changeset
2435     def test_changeset_update
2436       private_user = create(:user, :data_public => false)
2437       private_changeset = create(:changeset, :user => private_user)
2438       user = create(:user)
2439       changeset = create(:changeset, :user => user)
2440
2441       ## First try with a non-public user
2442       new_changeset = create_changeset_xml(:user => private_user)
2443       new_tag = XML::Node.new "tag"
2444       new_tag["k"] = "tagtesting"
2445       new_tag["v"] = "valuetesting"
2446       new_changeset.find("//osm/changeset").first << new_tag
2447
2448       # try without any authorization
2449       put changeset_show_path(private_changeset), :params => new_changeset.to_s
2450       assert_response :unauthorized
2451
2452       # try with the wrong authorization
2453       auth_header = bearer_authorization_header
2454       put changeset_show_path(private_changeset), :params => new_changeset.to_s, :headers => auth_header
2455       assert_response :conflict
2456
2457       # now this should get an unauthorized
2458       auth_header = bearer_authorization_header private_user
2459       put changeset_show_path(private_changeset), :params => new_changeset.to_s, :headers => auth_header
2460       assert_require_public_data "user with their data non-public, shouldn't be able to edit their changeset"
2461
2462       ## Now try with the public user
2463       new_changeset = create_changeset_xml(:id => 1)
2464       new_tag = XML::Node.new "tag"
2465       new_tag["k"] = "tagtesting"
2466       new_tag["v"] = "valuetesting"
2467       new_changeset.find("//osm/changeset").first << new_tag
2468
2469       # try without any authorization
2470       put changeset_show_path(changeset), :params => new_changeset.to_s
2471       assert_response :unauthorized
2472
2473       # try with the wrong authorization
2474       auth_header = bearer_authorization_header
2475       put changeset_show_path(changeset), :params => new_changeset.to_s, :headers => auth_header
2476       assert_response :conflict
2477
2478       # now this should work...
2479       auth_header = bearer_authorization_header user
2480       put changeset_show_path(changeset), :params => new_changeset.to_s, :headers => auth_header
2481       assert_response :success
2482
2483       assert_select "osm>changeset[id='#{changeset.id}']", 1
2484       assert_select "osm>changeset>tag", 1
2485       assert_select "osm>changeset>tag[k='tagtesting'][v='valuetesting']", 1
2486     end
2487
2488     ##
2489     # check that a user different from the one who opened the changeset
2490     # can't modify it.
2491     def test_changeset_update_invalid
2492       auth_header = bearer_authorization_header
2493
2494       changeset = create(:changeset)
2495       new_changeset = create_changeset_xml(:user => changeset.user, :id => changeset.id)
2496       new_tag = XML::Node.new "tag"
2497       new_tag["k"] = "testing"
2498       new_tag["v"] = "testing"
2499       new_changeset.find("//osm/changeset").first << new_tag
2500
2501       put changeset_show_path(changeset), :params => new_changeset.to_s, :headers => auth_header
2502       assert_response :conflict
2503     end
2504
2505     ##
2506     # check that a changeset can contain a certain max number of changes.
2507     ## FIXME should be changed to an integration test due to the with_controller
2508     def test_changeset_limits
2509       user = create(:user)
2510       auth_header = bearer_authorization_header user
2511
2512       # create an old changeset to ensure we have the maximum rate limit
2513       create(:changeset, :user => user, :created_at => Time.now.utc - 28.days)
2514
2515       # open a new changeset
2516       xml = "<osm><changeset/></osm>"
2517       post api_changesets_path, :params => xml, :headers => auth_header
2518       assert_response :success, "can't create a new changeset"
2519       cs_id = @response.body.to_i
2520
2521       # start the counter just short of where the changeset should finish.
2522       offset = 10
2523       # alter the database to set the counter on the changeset directly,
2524       # otherwise it takes about 6 minutes to fill all of them.
2525       changeset = Changeset.find(cs_id)
2526       changeset.num_changes = Changeset::MAX_ELEMENTS - offset
2527       changeset.save!
2528
2529       with_controller(NodesController.new) do
2530         # create a new node
2531         xml = "<osm><node changeset='#{cs_id}' lat='0.0' lon='0.0'/></osm>"
2532         post api_nodes_path, :params => xml, :headers => auth_header
2533         assert_response :success, "can't create a new node"
2534         node_id = @response.body.to_i
2535
2536         get api_node_path(node_id)
2537         assert_response :success, "can't read back new node"
2538         node_doc = XML::Parser.string(@response.body).parse
2539         node_xml = node_doc.find("//osm/node").first
2540
2541         # loop until we fill the changeset with nodes
2542         offset.times do |i|
2543           node_xml["lat"] = rand.to_s
2544           node_xml["lon"] = rand.to_s
2545           node_xml["version"] = (i + 1).to_s
2546
2547           put api_node_path(node_id), :params => node_doc.to_s, :headers => auth_header
2548           assert_response :success, "attempt #{i} should have succeeded"
2549         end
2550
2551         # trying again should fail
2552         node_xml["lat"] = rand.to_s
2553         node_xml["lon"] = rand.to_s
2554         node_xml["version"] = offset.to_s
2555
2556         put api_node_path(node_id), :params => node_doc.to_s, :headers => auth_header
2557         assert_response :conflict, "final attempt should have failed"
2558       end
2559
2560       changeset = Changeset.find(cs_id)
2561       assert_equal Changeset::MAX_ELEMENTS + 1, changeset.num_changes
2562
2563       # check that the changeset is now closed as well
2564       assert_not(changeset.open?,
2565                  "changeset should have been auto-closed by exceeding " \
2566                  "element limit.")
2567     end
2568
2569     ##
2570     # check that the changeset download for a changeset with a redacted
2571     # element in it doesn't contain that element.
2572     def test_diff_download_redacted
2573       changeset = create(:changeset)
2574       node = create(:node, :with_history, :version => 2, :changeset => changeset)
2575       node_v1 = node.old_nodes.find_by(:version => 1)
2576       node_v1.redact!(create(:redaction))
2577
2578       get changeset_download_path(changeset)
2579       assert_response :success
2580
2581       assert_select "osmChange", 1
2582       # this changeset contains the node in versions 1 & 2, but 1 should
2583       # be hidden.
2584       assert_select "osmChange node[id='#{node.id}']", 1
2585       assert_select "osmChange node[id='#{node.id}'][version='1']", 0
2586     end
2587
2588     ##
2589     # test subscribe success
2590     def test_subscribe_success
2591       auth_header = bearer_authorization_header
2592       changeset = create(:changeset, :closed)
2593
2594       assert_difference "changeset.subscribers.count", 1 do
2595         post api_changeset_subscribe_path(changeset), :headers => auth_header
2596       end
2597       assert_response :success
2598
2599       # not closed changeset
2600       changeset = create(:changeset)
2601       assert_difference "changeset.subscribers.count", 1 do
2602         post api_changeset_subscribe_path(changeset), :headers => auth_header
2603       end
2604       assert_response :success
2605     end
2606
2607     ##
2608     # test subscribe fail
2609     def test_subscribe_fail
2610       user = create(:user)
2611
2612       # unauthorized
2613       changeset = create(:changeset, :closed)
2614       assert_no_difference "changeset.subscribers.count" do
2615         post api_changeset_subscribe_path(changeset)
2616       end
2617       assert_response :unauthorized
2618
2619       auth_header = bearer_authorization_header user
2620
2621       # bad changeset id
2622       assert_no_difference "changeset.subscribers.count" do
2623         post api_changeset_subscribe_path(999111), :headers => auth_header
2624       end
2625       assert_response :not_found
2626
2627       # trying to subscribe when already subscribed
2628       changeset = create(:changeset, :closed)
2629       changeset.subscribers.push(user)
2630       assert_no_difference "changeset.subscribers.count" do
2631         post api_changeset_subscribe_path(changeset), :headers => auth_header
2632       end
2633       assert_response :conflict
2634     end
2635
2636     ##
2637     # test unsubscribe success
2638     def test_unsubscribe_success
2639       user = create(:user)
2640       auth_header = bearer_authorization_header user
2641       changeset = create(:changeset, :closed)
2642       changeset.subscribers.push(user)
2643
2644       assert_difference "changeset.subscribers.count", -1 do
2645         post api_changeset_unsubscribe_path(changeset), :headers => auth_header
2646       end
2647       assert_response :success
2648
2649       # not closed changeset
2650       changeset = create(:changeset)
2651       changeset.subscribers.push(user)
2652
2653       assert_difference "changeset.subscribers.count", -1 do
2654         post api_changeset_unsubscribe_path(changeset), :headers => auth_header
2655       end
2656       assert_response :success
2657     end
2658
2659     ##
2660     # test unsubscribe fail
2661     def test_unsubscribe_fail
2662       # unauthorized
2663       changeset = create(:changeset, :closed)
2664       assert_no_difference "changeset.subscribers.count" do
2665         post api_changeset_unsubscribe_path(changeset)
2666       end
2667       assert_response :unauthorized
2668
2669       auth_header = bearer_authorization_header
2670
2671       # bad changeset id
2672       assert_no_difference "changeset.subscribers.count" do
2673         post api_changeset_unsubscribe_path(999111), :headers => auth_header
2674       end
2675       assert_response :not_found
2676
2677       # trying to unsubscribe when not subscribed
2678       changeset = create(:changeset, :closed)
2679       assert_no_difference "changeset.subscribers.count" do
2680         post api_changeset_unsubscribe_path(changeset), :headers => auth_header
2681       end
2682       assert_response :not_found
2683     end
2684
2685     private
2686
2687     ##
2688     # check that the output consists of one specific changeset
2689     def assert_single_changeset(changeset, &)
2690       assert_dom "> changeset", 1 do
2691         assert_dom "> @id", changeset.id.to_s
2692         assert_dom "> @created_at", changeset.created_at.xmlschema
2693         if changeset.open?
2694           assert_dom "> @open", "true"
2695           assert_dom "> @closed_at", 0
2696         else
2697           assert_dom "> @open", "false"
2698           assert_dom "> @closed_at", changeset.closed_at.xmlschema
2699         end
2700         assert_dom "> @comments_count", changeset.comments.length.to_s
2701         assert_dom "> @changes_count", changeset.num_changes.to_s
2702         yield if block_given?
2703       end
2704     end
2705
2706     def assert_single_changeset_json(changeset, js)
2707       assert_equal changeset.id, js["changeset"]["id"]
2708       assert_equal changeset.created_at.xmlschema, js["changeset"]["created_at"]
2709       if changeset.open?
2710         assert js["changeset"]["open"]
2711         assert_nil js["changeset"]["closed_at"]
2712       else
2713         assert_not js["changeset"]["open"]
2714         assert_equal changeset.closed_at.xmlschema, js["changeset"]["closed_at"]
2715       end
2716       assert_equal changeset.comments.length, js["changeset"]["comments_count"]
2717       assert_equal changeset.num_changes, js["changeset"]["changes_count"]
2718     end
2719
2720     ##
2721     # check that certain changesets exist in the output in the specified order
2722     def assert_changesets_in_order(changesets)
2723       assert_select "osm>changeset", changesets.size
2724       changesets.each_with_index do |changeset, index|
2725         assert_select "osm>changeset:nth-child(#{index + 1})[id='#{changeset.id}']", 1
2726       end
2727     end
2728
2729     ##
2730     # update the changeset_id of a way element
2731     def update_changeset(xml, changeset_id)
2732       xml_attr_rewrite(xml, "changeset", changeset_id)
2733     end
2734
2735     ##
2736     # update an attribute in a way element
2737     def xml_attr_rewrite(xml, name, value)
2738       xml.find("//osm/way").first[name] = value.to_s
2739       xml
2740     end
2741
2742     ##
2743     # build XML for changesets
2744     def create_changeset_xml(user: nil, id: nil)
2745       root = XML::Document.new
2746       root.root = XML::Node.new "osm"
2747       cs = XML::Node.new "changeset"
2748       if user
2749         cs["user"] = user.display_name
2750         cs["uid"] = user.id.to_s
2751       end
2752       cs["id"] = id.to_s if id
2753       root.root << cs
2754       root
2755     end
2756   end
2757 end