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