]> git.openstreetmap.org Git - rails.git/blob - test/controllers/api/old_nodes_controller_test.rb
Add patch/put api message update
[rails.git] / test / controllers / api / old_nodes_controller_test.rb
1 require "test_helper"
2
3 module Api
4   class OldNodesControllerTest < ActionDispatch::IntegrationTest
5     #
6     # TODO: test history
7     #
8
9     ##
10     # test all routes which lead to this controller
11     def test_routes
12       assert_routing(
13         { :path => "/api/0.6/node/1/history", :method => :get },
14         { :controller => "api/old_nodes", :action => "history", :id => "1" }
15       )
16       assert_routing(
17         { :path => "/api/0.6/node/1/2", :method => :get },
18         { :controller => "api/old_nodes", :action => "show", :id => "1", :version => "2" }
19       )
20       assert_routing(
21         { :path => "/api/0.6/node/1/history.json", :method => :get },
22         { :controller => "api/old_nodes", :action => "history", :id => "1", :format => "json" }
23       )
24       assert_routing(
25         { :path => "/api/0.6/node/1/2.json", :method => :get },
26         { :controller => "api/old_nodes", :action => "show", :id => "1", :version => "2", :format => "json" }
27       )
28       assert_routing(
29         { :path => "/api/0.6/node/1/2/redact", :method => :post },
30         { :controller => "api/old_nodes", :action => "redact", :id => "1", :version => "2" }
31       )
32     end
33
34     ##
35     # test the version call by submitting several revisions of a new node
36     # to the API and ensuring that later calls to version return the
37     # matching versions of the object.
38     #
39     ##
40     # FIXME: Move this test to being an integration test since it spans multiple controllers
41     def test_version
42       private_user = create(:user, :data_public => false)
43       private_node = create(:node, :with_history, :version => 4, :lat => 0, :lon => 0, :changeset => create(:changeset, :user => private_user))
44       user = create(:user)
45       node = create(:node, :with_history, :version => 4, :lat => 0, :lon => 0, :changeset => create(:changeset, :user => user))
46       create_list(:node_tag, 2, :node => node)
47       # Ensure that the current tags are propagated to the history too
48       propagate_tags(node, node.old_nodes.last)
49
50       ## First try this with a non-public user
51       auth_header = bearer_authorization_header private_user
52
53       # setup a simple XML node
54       xml_doc = xml_for_node(private_node)
55       xml_node = xml_doc.find("//osm/node").first
56       nodeid = private_node.id
57
58       # keep a hash of the versions => string, as we'll need something
59       # to test against later
60       versions = {}
61
62       # save a version for later checking
63       versions[xml_node["version"]] = xml_doc.to_s
64
65       # randomly move the node about
66       3.times do
67         # move the node somewhere else
68         xml_node["lat"] = precision(rand - 0.5).to_s
69         xml_node["lon"] = precision(rand - 0.5).to_s
70         with_controller(NodesController.new) do
71           put api_node_path(nodeid), :params => xml_doc.to_s, :headers => auth_header
72           assert_response :forbidden, "Should have rejected node update"
73           xml_node["version"] = @response.body.to_s
74         end
75         # save a version for later checking
76         versions[xml_node["version"]] = xml_doc.to_s
77       end
78
79       # add a bunch of random tags
80       3.times do
81         xml_tag = XML::Node.new("tag")
82         xml_tag["k"] = random_string
83         xml_tag["v"] = random_string
84         xml_node << xml_tag
85         with_controller(NodesController.new) do
86           put api_node_path(nodeid), :params => xml_doc.to_s, :headers => auth_header
87           assert_response :forbidden,
88                           "should have rejected node #{nodeid} (#{@response.body}) with forbidden"
89           xml_node["version"] = @response.body.to_s
90         end
91         # save a version for later checking
92         versions[xml_node["version"]] = xml_doc.to_s
93       end
94
95       # probably should check that they didn't get written to the database
96
97       ## Now do it with the public user
98       auth_header = bearer_authorization_header user
99
100       # setup a simple XML node
101
102       xml_doc = xml_for_node(node)
103       xml_node = xml_doc.find("//osm/node").first
104       nodeid = node.id
105
106       # keep a hash of the versions => string, as we'll need something
107       # to test against later
108       versions = {}
109
110       # save a version for later checking
111       versions[xml_node["version"]] = xml_doc.to_s
112
113       # randomly move the node about
114       3.times do
115         # move the node somewhere else
116         xml_node["lat"] = precision(rand - 0.5).to_s
117         xml_node["lon"] = precision(rand - 0.5).to_s
118         with_controller(NodesController.new) do
119           put api_node_path(nodeid), :params => xml_doc.to_s, :headers => auth_header
120           assert_response :success
121           xml_node["version"] = @response.body.to_s
122         end
123         # save a version for later checking
124         versions[xml_node["version"]] = xml_doc.to_s
125       end
126
127       # add a bunch of random tags
128       3.times do
129         xml_tag = XML::Node.new("tag")
130         xml_tag["k"] = random_string
131         xml_tag["v"] = random_string
132         xml_node << xml_tag
133         with_controller(NodesController.new) do
134           put api_node_path(nodeid), :params => xml_doc.to_s, :headers => auth_header
135           assert_response :success,
136                           "couldn't update node #{nodeid} (#{@response.body})"
137           xml_node["version"] = @response.body.to_s
138         end
139         # save a version for later checking
140         versions[xml_node["version"]] = xml_doc.to_s
141       end
142
143       # check all the versions
144       versions.each_key do |key|
145         get api_old_node_path(nodeid, key.to_i)
146
147         assert_response :success,
148                         "couldn't get version #{key.to_i} of node #{nodeid}"
149
150         check_node = Node.from_xml(versions[key])
151         api_node = Node.from_xml(@response.body.to_s)
152
153         assert_nodes_are_equal check_node, api_node
154       end
155     end
156
157     def test_not_found_version
158       check_not_found_id_version(70000, 312344)
159       check_not_found_id_version(-1, -13)
160       check_not_found_id_version(create(:node).id, 24354)
161       check_not_found_id_version(24356, create(:node).version)
162     end
163
164     ##
165     # Test that getting the current version is identical to picking
166     # that version with the version URI call.
167     def test_current_version
168       node = create(:node, :with_history)
169       used_node = create(:node, :with_history)
170       create(:way_node, :node => used_node)
171       node_used_by_relationship = create(:node, :with_history)
172       create(:relation_member, :member => node_used_by_relationship)
173       node_with_versions = create(:node, :with_history, :version => 4)
174
175       create(:node_tag, :node => node)
176       create(:node_tag, :node => used_node)
177       create(:node_tag, :node => node_used_by_relationship)
178       create(:node_tag, :node => node_with_versions)
179       propagate_tags(node, node.old_nodes.last)
180       propagate_tags(used_node, used_node.old_nodes.last)
181       propagate_tags(node_used_by_relationship, node_used_by_relationship.old_nodes.last)
182       propagate_tags(node_with_versions, node_with_versions.old_nodes.last)
183
184       check_current_version(node)
185       check_current_version(used_node)
186       check_current_version(node_used_by_relationship)
187       check_current_version(node_with_versions)
188     end
189
190     # Ensure the lat/lon is formatted as a decimal e.g. not 4.0e-05
191     def test_lat_lon_xml_format
192       old_node = create(:old_node, :latitude => (0.00004 * OldNode::SCALE).to_i, :longitude => (0.00008 * OldNode::SCALE).to_i)
193
194       get api_node_history_path(old_node.node_id)
195       assert_match(/lat="0.0000400"/, response.body)
196       assert_match(/lon="0.0000800"/, response.body)
197     end
198
199     ##
200     # test the redaction of an old version of a node, while not being
201     # authorised.
202     def test_redact_node_unauthorised
203       node = create(:node, :with_history, :version => 4)
204       node_v3 = node.old_nodes.find_by(:version => 3)
205
206       do_redact_node(node_v3,
207                      create(:redaction))
208       assert_response :unauthorized, "should need to be authenticated to redact."
209     end
210
211     ##
212     # test the redaction of an old version of a node, while being
213     # authorised as a normal user.
214     def test_redact_node_normal_user
215       auth_header = bearer_authorization_header
216
217       node = create(:node, :with_history, :version => 4)
218       node_v3 = node.old_nodes.find_by(:version => 3)
219
220       do_redact_node(node_v3,
221                      create(:redaction),
222                      auth_header)
223       assert_response :forbidden, "should need to be moderator to redact."
224     end
225
226     ##
227     # test that, even as moderator, the current version of a node
228     # can't be redacted.
229     def test_redact_node_current_version
230       auth_header = bearer_authorization_header create(:moderator_user)
231
232       node = create(:node, :with_history, :version => 4)
233       node_v4 = node.old_nodes.find_by(:version => 4)
234
235       do_redact_node(node_v4,
236                      create(:redaction),
237                      auth_header)
238       assert_response :bad_request, "shouldn't be OK to redact current version as moderator."
239     end
240
241     def test_redact_node_by_regular_without_write_redactions_scope
242       auth_header = bearer_authorization_header(create(:user), :scopes => %w[read_prefs write_api])
243       do_redact_redactable_node(auth_header)
244       assert_response :forbidden, "should need to be moderator to redact."
245     end
246
247     def test_redact_node_by_regular_with_write_redactions_scope
248       auth_header = bearer_authorization_header(create(:user), :scopes => %w[write_redactions])
249       do_redact_redactable_node(auth_header)
250       assert_response :forbidden, "should need to be moderator to redact."
251     end
252
253     def test_redact_node_by_moderator_without_write_redactions_scope
254       auth_header = bearer_authorization_header(create(:moderator_user), :scopes => %w[read_prefs write_api])
255       do_redact_redactable_node(auth_header)
256       assert_response :forbidden, "should need to have write_redactions scope to redact."
257     end
258
259     def test_redact_node_by_moderator_with_write_redactions_scope
260       auth_header = bearer_authorization_header(create(:moderator_user), :scopes => %w[write_redactions])
261       do_redact_redactable_node(auth_header)
262       assert_response :success, "should be OK to redact old version as moderator with write_redactions scope."
263     end
264
265     ##
266     # test that redacted nodes aren't visible, regardless of
267     # authorisation except as moderator...
268     def test_version_redacted
269       node = create(:node, :with_history, :version => 2)
270       node_v1 = node.old_nodes.find_by(:version => 1)
271       node_v1.redact!(create(:redaction))
272
273       get api_old_node_path(node_v1.node_id, node_v1.version)
274       assert_response :forbidden, "Redacted node shouldn't be visible via the version API."
275
276       # not even to a logged-in user
277       auth_header = bearer_authorization_header
278       get api_old_node_path(node_v1.node_id, node_v1.version), :headers => auth_header
279       assert_response :forbidden, "Redacted node shouldn't be visible via the version API, even when logged in."
280     end
281
282     ##
283     # test that redacted nodes aren't visible in the history
284     def test_history_redacted
285       node = create(:node, :with_history, :version => 2)
286       node_v1 = node.old_nodes.find_by(:version => 1)
287       node_v1.redact!(create(:redaction))
288
289       get api_node_history_path(node)
290       assert_response :success, "Redaction shouldn't have stopped history working."
291       assert_select "osm node[id='#{node_v1.node_id}'][version='#{node_v1.version}']", 0,
292                     "redacted node #{node_v1.node_id} version #{node_v1.version} shouldn't be present in the history."
293
294       # not even to a logged-in user
295       auth_header = bearer_authorization_header
296       get api_node_history_path(node), :headers => auth_header
297       assert_response :success, "Redaction shouldn't have stopped history working."
298       assert_select "osm node[id='#{node_v1.node_id}'][version='#{node_v1.version}']", 0,
299                     "redacted node #{node_v1.node_id} version #{node_v1.version} shouldn't be present in the history, even when logged in."
300     end
301
302     ##
303     # test the redaction of an old version of a node, while being
304     # authorised as a moderator.
305     def test_redact_node_moderator
306       node = create(:node, :with_history, :version => 4)
307       node_v3 = node.old_nodes.find_by(:version => 3)
308       auth_header = bearer_authorization_header create(:moderator_user)
309
310       do_redact_node(node_v3, create(:redaction), auth_header)
311       assert_response :success, "should be OK to redact old version as moderator."
312
313       # check moderator can still see the redacted data, when passing
314       # the appropriate flag
315       get api_old_node_path(node_v3.node_id, node_v3.version), :headers => auth_header
316       assert_response :forbidden, "After redaction, node should be gone for moderator, when flag not passed."
317       get api_old_node_path(node_v3.node_id, node_v3.version, :show_redactions => "true"), :headers => auth_header
318       assert_response :success, "After redaction, node should not be gone for moderator, when flag passed."
319
320       # and when accessed via history
321       get api_node_history_path(node)
322       assert_response :success, "Redaction shouldn't have stopped history working."
323       assert_select "osm node[id='#{node_v3.node_id}'][version='#{node_v3.version}']", 0,
324                     "node #{node_v3.node_id} version #{node_v3.version} should not be present in the history for moderators when not passing flag."
325       get api_node_history_path(node, :show_redactions => "true"), :headers => auth_header
326       assert_response :success, "Redaction shouldn't have stopped history working."
327       assert_select "osm node[id='#{node_v3.node_id}'][version='#{node_v3.version}']", 1,
328                     "node #{node_v3.node_id} version #{node_v3.version} should still be present in the history for moderators when passing flag."
329     end
330
331     # testing that if the moderator drops auth, he can't see the
332     # redacted stuff any more.
333     def test_redact_node_is_redacted
334       node = create(:node, :with_history, :version => 4)
335       node_v3 = node.old_nodes.find_by(:version => 3)
336       auth_header = bearer_authorization_header create(:moderator_user)
337
338       do_redact_node(node_v3, create(:redaction), auth_header)
339       assert_response :success, "should be OK to redact old version as moderator."
340
341       # re-auth as non-moderator
342       auth_header = bearer_authorization_header
343
344       # check can't see the redacted data
345       get api_old_node_path(node_v3.node_id, node_v3.version), :headers => auth_header
346       assert_response :forbidden, "Redacted node shouldn't be visible via the version API."
347
348       # and when accessed via history
349       get api_node_history_path(node), :headers => auth_header
350       assert_response :success, "Redaction shouldn't have stopped history working."
351       assert_select "osm node[id='#{node_v3.node_id}'][version='#{node_v3.version}']", 0,
352                     "redacted node #{node_v3.node_id} version #{node_v3.version} shouldn't be present in the history."
353     end
354
355     ##
356     # test the unredaction of an old version of a node, while not being
357     # authorised.
358     def test_unredact_node_unauthorised
359       node = create(:node, :with_history, :version => 2)
360       node_v1 = node.old_nodes.find_by(:version => 1)
361       node_v1.redact!(create(:redaction))
362
363       post node_version_redact_path(node_v1.node_id, node_v1.version)
364       assert_response :unauthorized, "should need to be authenticated to unredact."
365     end
366
367     ##
368     # test the unredaction of an old version of a node, while being
369     # authorised as a normal user.
370     def test_unredact_node_normal_user
371       user = create(:user)
372       node = create(:node, :with_history, :version => 2)
373       node_v1 = node.old_nodes.find_by(:version => 1)
374       node_v1.redact!(create(:redaction))
375
376       auth_header = bearer_authorization_header user
377
378       post node_version_redact_path(node_v1.node_id, node_v1.version), :headers => auth_header
379       assert_response :forbidden, "should need to be moderator to unredact."
380     end
381
382     ##
383     # test the unredaction of an old version of a node, while being
384     # authorised as a moderator.
385     def test_unredact_node_moderator
386       moderator_user = create(:moderator_user)
387       node = create(:node, :with_history, :version => 2)
388       node_v1 = node.old_nodes.find_by(:version => 1)
389       node_v1.redact!(create(:redaction))
390
391       auth_header = bearer_authorization_header moderator_user
392
393       post node_version_redact_path(node_v1.node_id, node_v1.version), :headers => auth_header
394       assert_response :success, "should be OK to unredact old version as moderator."
395
396       # check moderator can now see the redacted data, when not
397       # passing the aspecial flag
398       get api_old_node_path(node_v1.node_id, node_v1.version), :headers => auth_header
399       assert_response :success, "After unredaction, node should not be gone for moderator."
400
401       # and when accessed via history
402       get api_node_history_path(node)
403       assert_response :success, "Unredaction shouldn't have stopped history working."
404       assert_select "osm node[id='#{node_v1.node_id}'][version='#{node_v1.version}']", 1,
405                     "node #{node_v1.node_id} version #{node_v1.version} should now be present in the history for moderators without passing flag."
406
407       auth_header = bearer_authorization_header
408
409       # check normal user can now see the redacted data
410       get api_old_node_path(node_v1.node_id, node_v1.version), :headers => auth_header
411       assert_response :success, "After unredaction, node should be visible to normal users."
412
413       # and when accessed via history
414       get api_node_history_path(node)
415       assert_response :success, "Unredaction shouldn't have stopped history working."
416       assert_select "osm node[id='#{node_v1.node_id}'][version='#{node_v1.version}']", 1,
417                     "node #{node_v1.node_id} version #{node_v1.version} should now be present in the history for normal users without passing flag."
418     end
419
420     private
421
422     def do_redact_redactable_node(headers = {})
423       node = create(:node, :with_history, :version => 4)
424       node_v3 = node.old_nodes.find_by(:version => 3)
425       do_redact_node(node_v3, create(:redaction), headers)
426     end
427
428     def do_redact_node(node, redaction, headers = {})
429       get api_old_node_path(node.node_id, node.version), :headers => headers
430       assert_response :success, "should be able to get version #{node.version} of node #{node.node_id}."
431
432       # now redact it
433       post node_version_redact_path(node.node_id, node.version), :params => { :redaction => redaction.id }, :headers => headers
434     end
435
436     def check_current_version(node_id)
437       # get the current version of the node
438       current_node = with_controller(NodesController.new) do
439         get api_node_path(node_id)
440         assert_response :success, "cant get current node #{node_id}"
441         Node.from_xml(@response.body)
442       end
443       assert_not_nil current_node, "getting node #{node_id} returned nil"
444
445       # get the "old" version of the node from the old_node interface
446       get api_old_node_path(node_id, current_node.version)
447       assert_response :success, "cant get old node #{node_id}, v#{current_node.version}"
448       old_node = Node.from_xml(@response.body)
449
450       # check the nodes are the same
451       assert_nodes_are_equal current_node, old_node
452     end
453
454     def check_not_found_id_version(id, version)
455       get api_old_node_path(id, version)
456       assert_response :not_found
457     rescue ActionController::UrlGenerationError => e
458       assert_match(/No route matches/, e.to_s)
459     end
460
461     ##
462     # returns a 16 character long string with some nasty characters in it.
463     # this ought to stress-test the tag handling as well as the versioning.
464     def random_string
465       letters = [["!", '"', "$", "&", ";", "@"],
466                  ("a".."z").to_a,
467                  ("A".."Z").to_a,
468                  ("0".."9").to_a].flatten
469       (1..16).map { letters[rand(letters.length)] }.join
470     end
471
472     ##
473     # truncate a floating point number to the scale that it is stored in
474     # the database. otherwise rounding errors can produce failing unit
475     # tests when they shouldn't.
476     def precision(f)
477       (f * GeoRecord::SCALE).round.to_f / GeoRecord::SCALE
478     end
479
480     def propagate_tags(node, old_node)
481       node.tags.each do |k, v|
482         create(:old_node_tag, :old_node => old_node, :k => k, :v => v)
483       end
484     end
485   end
486 end