source: subversion/sites/rails_port/test/functional/node_controller_test.rb @ 14724

Last change on this file since 14724 was 14586, checked in by tomhughes, 10 years ago

Merge api06 branch to trunk.

File size: 10.7 KB
Line 
1require File.dirname(__FILE__) + '/../test_helper'
2
3class NodeControllerTest < ActionController::TestCase
4  api_fixtures
5
6  def test_create
7    # cannot read password from fixture as it is stored as MD5 digest
8    basic_authorization(users(:normal_user).email, "test")
9   
10    # create a node with random lat/lon
11    lat = rand(100)-50 + rand
12    lon = rand(100)-50 + rand
13    # normal user has a changeset open, so we'll use that.
14    changeset = changesets(:normal_user_first_change)
15    # create a minimal xml file
16    content("<osm><node lat='#{lat}' lon='#{lon}' changeset='#{changeset.id}'/></osm>")
17    put :create
18    # hope for success
19    assert_response :success, "node upload did not return success status"
20
21    # read id of created node and search for it
22    nodeid = @response.body
23    checknode = Node.find(nodeid)
24    assert_not_nil checknode, "uploaded node not found in data base after upload"
25    # compare values
26    assert_in_delta lat * 10000000, checknode.latitude, 1, "saved node does not match requested latitude"
27    assert_in_delta lon * 10000000, checknode.longitude, 1, "saved node does not match requested longitude"
28    assert_equal changesets(:normal_user_first_change).id, checknode.changeset_id, "saved node does not belong to changeset that it was created in"
29    assert_equal true, checknode.visible, "saved node is not visible"
30  end
31
32  def test_create_invalid_xml
33    # Initial setup
34    basic_authorization(users(:normal_user).email, "test")
35    # normal user has a changeset open, so we'll use that.
36    changeset = changesets(:normal_user_first_change)
37    lat = 3.434
38    lon = 3.23
39   
40    # test that the upload is rejected when no lat is supplied
41    # create a minimal xml file
42    content("<osm><node lon='#{lon}' changeset='#{changeset.id}'/></osm>")
43    put :create
44    # hope for success
45    assert_response :bad_request, "node upload did not return bad_request status"
46    assert_equal 'Cannot parse valid node from xml string <node lon="3.23" changeset="1"/>. lat missing', @response.body
47
48    # test that the upload is rejected when no lon is supplied
49    # create a minimal xml file
50    content("<osm><node lat='#{lat}' changeset='#{changeset.id}'/></osm>")
51    put :create
52    # hope for success
53    assert_response :bad_request, "node upload did not return bad_request status"
54    assert_equal 'Cannot parse valid node from xml string <node lat="3.434" changeset="1"/>. lon missing', @response.body
55
56  end
57
58  def test_read
59    # check that a visible node is returned properly
60    get :read, :id => current_nodes(:visible_node).id
61    assert_response :success
62
63    # check that an invisible node is not returned
64    get :read, :id => current_nodes(:invisible_node).id
65    assert_response :gone
66
67    # check chat a non-existent node is not returned
68    get :read, :id => 0
69    assert_response :not_found
70  end
71
72  # this tests deletion restrictions - basic deletion is tested in the unit
73  # tests for node!
74  def test_delete
75    # first try to delete node without auth
76    delete :delete, :id => current_nodes(:visible_node).id
77    assert_response :unauthorized
78
79    # now set auth
80    basic_authorization(users(:normal_user).email, "test"); 
81
82    # try to delete with an invalid (closed) changeset
83    content update_changeset(current_nodes(:visible_node).to_xml,
84                             changesets(:normal_user_closed_change).id)
85    delete :delete, :id => current_nodes(:visible_node).id
86    assert_response :conflict
87
88    # try to delete with an invalid (non-existent) changeset
89    content update_changeset(current_nodes(:visible_node).to_xml,0)
90    delete :delete, :id => current_nodes(:visible_node).id
91    assert_response :conflict
92
93    # valid delete now takes a payload
94    content(nodes(:visible_node).to_xml)
95    delete :delete, :id => current_nodes(:visible_node).id
96    assert_response :success
97
98    # valid delete should return the new version number, which should
99    # be greater than the old version number
100    assert @response.body.to_i > current_nodes(:visible_node).version,
101       "delete request should return a new version number for node"
102
103    # this won't work since the node is already deleted
104    content(nodes(:invisible_node).to_xml)
105    delete :delete, :id => current_nodes(:invisible_node).id
106    assert_response :gone
107
108    # this won't work since the node never existed
109    delete :delete, :id => 0
110    assert_response :not_found
111
112    ## these test whether nodes which are in-use can be deleted:
113    # in a way...
114    content(nodes(:used_node_1).to_xml)
115    delete :delete, :id => current_nodes(:used_node_1).id
116    assert_response :precondition_failed,
117       "shouldn't be able to delete a node used in a way (#{@response.body})"
118
119    # in a relation...
120    content(nodes(:node_used_by_relationship).to_xml)
121    delete :delete, :id => current_nodes(:node_used_by_relationship).id
122    assert_response :precondition_failed,
123       "shouldn't be able to delete a node used in a relation (#{@response.body})"
124  end
125
126  ##
127  # tests whether the API works and prevents incorrect use while trying
128  # to update nodes.
129  def test_update
130    # try and update a node without authorisation
131    # first try to delete node without auth
132    content current_nodes(:visible_node).to_xml
133    put :update, :id => current_nodes(:visible_node).id
134    assert_response :unauthorized
135   
136    # setup auth
137    basic_authorization(users(:normal_user).email, "test")
138
139    ## trying to break changesets
140
141    # try and update in someone else's changeset
142    content update_changeset(current_nodes(:visible_node).to_xml,
143                             changesets(:second_user_first_change).id)
144    put :update, :id => current_nodes(:visible_node).id
145    assert_response :conflict, "update with other user's changeset should be rejected"
146
147    # try and update in a closed changeset
148    content update_changeset(current_nodes(:visible_node).to_xml,
149                             changesets(:normal_user_closed_change).id)
150    put :update, :id => current_nodes(:visible_node).id
151    assert_response :conflict, "update with closed changeset should be rejected"
152
153    # try and update in a non-existant changeset
154    content update_changeset(current_nodes(:visible_node).to_xml, 0)
155    put :update, :id => current_nodes(:visible_node).id
156    assert_response :conflict, "update with changeset=0 should be rejected"
157
158    ## try and submit invalid updates
159    content xml_attr_rewrite(current_nodes(:visible_node).to_xml, 'lat', 91.0);
160    put :update, :id => current_nodes(:visible_node).id
161    assert_response :bad_request, "node at lat=91 should be rejected"
162
163    content xml_attr_rewrite(current_nodes(:visible_node).to_xml, 'lat', -91.0);
164    put :update, :id => current_nodes(:visible_node).id
165    assert_response :bad_request, "node at lat=-91 should be rejected"
166   
167    content xml_attr_rewrite(current_nodes(:visible_node).to_xml, 'lon', 181.0);
168    put :update, :id => current_nodes(:visible_node).id
169    assert_response :bad_request, "node at lon=181 should be rejected"
170
171    content xml_attr_rewrite(current_nodes(:visible_node).to_xml, 'lon', -181.0);
172    put :update, :id => current_nodes(:visible_node).id
173    assert_response :bad_request, "node at lon=-181 should be rejected"
174
175    ## next, attack the versioning
176    current_node_version = current_nodes(:visible_node).version
177
178    # try and submit a version behind
179    content xml_attr_rewrite(current_nodes(:visible_node).to_xml, 
180                             'version', current_node_version - 1);
181    put :update, :id => current_nodes(:visible_node).id
182    assert_response :conflict, "should have failed on old version number"
183   
184    # try and submit a version ahead
185    content xml_attr_rewrite(current_nodes(:visible_node).to_xml, 
186                             'version', current_node_version + 1);
187    put :update, :id => current_nodes(:visible_node).id
188    assert_response :conflict, "should have failed on skipped version number"
189
190    # try and submit total crap in the version field
191    content xml_attr_rewrite(current_nodes(:visible_node).to_xml, 
192                             'version', 'p1r4t3s!');
193    put :update, :id => current_nodes(:visible_node).id
194    assert_response :conflict, 
195       "should not be able to put 'p1r4at3s!' in the version field"
196   
197    ## finally, produce a good request which should work
198    content current_nodes(:visible_node).to_xml
199    put :update, :id => current_nodes(:visible_node).id
200    assert_response :success, "a valid update request failed"
201  end
202
203  ##
204  # test adding tags to a node
205  def test_duplicate_tags
206    # setup auth
207    basic_authorization(users(:normal_user).email, "test")
208
209    # add an identical tag to the node
210    tag_xml = XML::Node.new("tag")
211    tag_xml['k'] = current_node_tags(:t1).k
212    tag_xml['v'] = current_node_tags(:t1).v
213
214    # add the tag into the existing xml
215    node_xml = current_nodes(:visible_node).to_xml
216    node_xml.find("//osm/node").first << tag_xml
217
218    # try and upload it
219    content node_xml
220    put :update, :id => current_nodes(:visible_node).id
221    assert_response :bad_request, 
222      "adding duplicate tags to a node should fail with 'bad request'"
223    assert_equal "Element node/#{current_nodes(:visible_node).id} has duplicate tags with key #{current_node_tags(:t1).k}.", @response.body
224  end
225
226  # test whether string injection is possible
227  def test_string_injection
228    basic_authorization(users(:normal_user).email, "test")
229    changeset_id = changesets(:normal_user_first_change).id
230
231    # try and put something into a string that the API might
232    # use unquoted and therefore allow code injection...
233    content "<osm><node lat='0' lon='0' changeset='#{changeset_id}'>" +
234      '<tag k="#{@user.inspect}" v="0"/>' +
235      '</node></osm>'
236    put :create
237    assert_response :success
238    nodeid = @response.body
239
240    # find the node in the database
241    checknode = Node.find(nodeid)
242    assert_not_nil checknode, "node not found in data base after upload"
243   
244    # and grab it using the api
245    get :read, :id => nodeid
246    assert_response :success
247    apinode = Node.from_xml(@response.body)
248    assert_not_nil apinode, "downloaded node is nil, but shouldn't be"
249   
250    # check the tags are not corrupted
251    assert_equal checknode.tags, apinode.tags
252    assert apinode.tags.include?('#{@user.inspect}')
253  end
254
255  def basic_authorization(user, pass)
256    @request.env["HTTP_AUTHORIZATION"] = "Basic %s" % Base64.encode64("#{user}:#{pass}")
257  end
258
259  def content(c)
260    @request.env["RAW_POST_DATA"] = c.to_s
261  end
262
263  ##
264  # update the changeset_id of a node element
265  def update_changeset(xml, changeset_id)
266    xml_attr_rewrite(xml, 'changeset', changeset_id)
267  end
268
269  ##
270  # update an attribute in the node element
271  def xml_attr_rewrite(xml, name, value)
272    xml.find("//osm/node").first[name] = value.to_s
273    return xml
274  end
275
276  ##
277  # parse some xml
278  def xml_parse(xml)
279    parser = XML::Parser.string(xml)
280    parser.parse
281  end
282end
Note: See TracBrowser for help on using the repository browser.