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