Altered a functional test on way_tags.
[rails.git] / test / functional / way_controller_test.rb
1 require File.dirname(__FILE__) + '/../test_helper'
2 require 'way_controller'
3
4 # Re-raise errors caught by the controller.
5 class WayController; def rescue_action(e) raise e end; end
6
7 class WayControllerTest < Test::Unit::TestCase
8   api_fixtures
9
10   def setup
11     @controller = WayController.new
12     @request    = ActionController::TestRequest.new
13     @response   = ActionController::TestResponse.new
14   end
15
16   def basic_authorization(user, pass)
17     @request.env["HTTP_AUTHORIZATION"] = "Basic %s" % Base64.encode64("#{user}:#{pass}")
18   end
19
20   def content(c)
21     @request.env["RAW_POST_DATA"] = c.to_s
22   end
23
24   # -------------------------------------
25   # Test reading ways.
26   # -------------------------------------
27
28   def test_read
29     # check that a visible way is returned properly
30     get :read, :id => current_ways(:visible_way).id
31     assert_response :success
32
33     # check that an invisible way is not returned
34     get :read, :id => current_ways(:invisible_way).id
35     assert_response :gone
36
37     # check chat a non-existent way is not returned
38     get :read, :id => 0
39     assert_response :not_found
40
41     # check the "ways for node" mode
42     get :ways_for_node, :id => current_nodes(:used_node_1).id
43     assert_response :success
44     # FIXME check whether this contains the stuff we want!
45     #print @response.body
46     # Needs to be updated when changing fixtures
47     # The generator should probably be defined in the environment.rb file
48     # in the same place as the api version
49     assert_select "osm[version=#{API_VERSION}][generator=\"OpenStreetMap server\"]", 1
50     assert_select "osm way", 3
51     assert_select "osm way nd", 3
52     assert_select "osm way tag", 3
53
54     # check the "full" mode
55     get :full, :id => current_ways(:visible_way).id
56     assert_response :success
57     # FIXME check whether this contains the stuff we want!
58     #print @response.body
59     # Check the way is correctly returned
60     way = current_ways(:visible_way)
61     assert_select "osm way[id=#{way.id}][version=#{way.version}][visible=#{way.visible}]", 1
62     assert_select "osm way nd[ref=#{way.way_nodes[0].node_id}]", 1
63     # Check that the node is correctly returned
64     nd = current_ways(:visible_way).nodes
65     assert_equal 1, nd.count
66     nda = nd[0]
67     assert_select "osm node[id=#{nda.id}][version=#{nda.version}][lat=#{nda.lat}][lon=#{nda.lon}]", 1 
68   end
69
70   # -------------------------------------
71   # Test simple way creation.
72   # -------------------------------------
73
74   def test_create
75     nid1 = current_nodes(:used_node_1).id
76     nid2 = current_nodes(:used_node_2).id
77     basic_authorization "test@openstreetmap.org", "test"
78
79     # use the first user's open changeset
80     changeset_id = changesets(:normal_user_first_change).id
81     
82     # create a way with pre-existing nodes
83     content "<osm><way changeset='#{changeset_id}'>" +
84       "<nd ref='#{nid1}'/><nd ref='#{nid2}'/>" + 
85       "<tag k='test' v='yes' /></way></osm>"
86     put :create
87     # hope for success
88     assert_response :success, 
89         "way upload did not return success status"
90     # read id of created way and search for it
91     wayid = @response.body
92     checkway = Way.find(wayid)
93     assert_not_nil checkway, 
94         "uploaded way not found in data base after upload"
95     # compare values
96     assert_equal checkway.nds.length, 2, 
97         "saved way does not contain exactly one node"
98     assert_equal checkway.nds[0], nid1, 
99         "saved way does not contain the right node on pos 0"
100     assert_equal checkway.nds[1], nid2, 
101         "saved way does not contain the right node on pos 1"
102     assert_equal checkway.changeset_id, changeset_id,
103         "saved way does not belong to the correct changeset"
104     assert_equal users(:normal_user).id, checkway.changeset.user_id, 
105         "saved way does not belong to user that created it"
106     assert_equal true, checkway.visible, 
107         "saved way is not visible"
108   end
109
110   # -------------------------------------
111   # Test creating some invalid ways.
112   # -------------------------------------
113
114   def test_create_invalid
115     basic_authorization "test@openstreetmap.org", "test"
116
117     # use the first user's open changeset
118     open_changeset_id = changesets(:normal_user_first_change).id
119     closed_changeset_id = changesets(:normal_user_closed_change).id
120     nid1 = current_nodes(:used_node_1).id
121
122     # create a way with non-existing node
123     content "<osm><way changeset='#{open_changeset_id}'>" + 
124       "<nd ref='0'/><tag k='test' v='yes' /></way></osm>"
125     put :create
126     # expect failure
127     assert_response :precondition_failed, 
128         "way upload with invalid node did not return 'precondition failed'"
129
130     # create a way with no nodes
131     content "<osm><way changeset='#{open_changeset_id}'>" +
132       "<tag k='test' v='yes' /></way></osm>"
133     put :create
134     # expect failure
135     assert_response :precondition_failed, 
136         "way upload with no node did not return 'precondition failed'"
137
138     # create a way inside a closed changeset
139     content "<osm><way changeset='#{closed_changeset_id}'>" +
140       "<nd ref='#{nid1}'/></way></osm>"
141     put :create
142     # expect failure
143     assert_response :conflict, 
144         "way upload to closed changeset did not return 'conflict'"    
145   end
146
147   # -------------------------------------
148   # Test deleting ways.
149   # -------------------------------------
150   
151   def test_delete
152     # first try to delete way without auth
153     delete :delete, :id => current_ways(:visible_way).id
154     assert_response :unauthorized
155
156     # now set auth
157     basic_authorization("test@openstreetmap.org", "test");  
158
159     # this shouldn't work as with the 0.6 api we need pay load to delete
160     delete :delete, :id => current_ways(:visible_way).id
161     assert_response :bad_request
162     
163     # Now try without having a changeset
164     content "<osm><way id='#{current_ways(:visible_way).id}'></osm>"
165     delete :delete, :id => current_ways(:visible_way).id
166     assert_response :bad_request
167     
168     # try to delete with an invalid (closed) changeset
169     content update_changeset(current_ways(:visible_way).to_xml,
170                              changesets(:normal_user_closed_change).id)
171     delete :delete, :id => current_ways(:visible_way).id
172     assert_response :conflict
173
174     # try to delete with an invalid (non-existent) changeset
175     content update_changeset(current_ways(:visible_way).to_xml,0)
176     delete :delete, :id => current_ways(:visible_way).id
177     assert_response :conflict
178
179     # Now try with a valid changeset
180     content current_ways(:visible_way).to_xml
181     delete :delete, :id => current_ways(:visible_way).id
182     assert_response :success
183
184     # check the returned value - should be the new version number
185     # valid delete should return the new version number, which should
186     # be greater than the old version number
187     assert @response.body.to_i > current_ways(:visible_way).version,
188        "delete request should return a new version number for way"
189
190     # this won't work since the way is already deleted
191     content current_ways(:invisible_way).to_xml
192     delete :delete, :id => current_ways(:invisible_way).id
193     assert_response :gone
194
195     # this shouldn't work as the way is used in a relation
196     content current_ways(:used_way).to_xml
197     delete :delete, :id => current_ways(:used_way).id
198     assert_response :precondition_failed, 
199        "shouldn't be able to delete a way used in a relation (#{@response.body})"
200
201     # this won't work since the way never existed
202     delete :delete, :id => 0
203     assert_response :not_found
204   end
205
206   # ------------------------------------------------------------
207   # test tags handling
208   # ------------------------------------------------------------
209
210   ##
211   # Try adding a duplicate of an existing tag to a way
212   def test_add_duplicate_tags
213     # setup auth
214     basic_authorization(users(:normal_user).email, "test")
215
216     # add an identical tag to the way
217     tag_xml = XML::Node.new("tag")
218     tag_xml['k'] = current_way_tags(:t1).k
219     tag_xml['v'] = current_way_tags(:t1).v
220
221     # add the tag into the existing xml
222     way_xml = current_ways(:visible_way).to_xml
223     way_xml.find("//osm/way").first << tag_xml
224
225     # try and upload it
226     content way_xml
227     put :update, :id => current_ways(:visible_way).id
228     assert_response :bad_request, 
229        "adding a duplicate tag to a way should fail with 'bad request'"
230   end
231
232   ##
233   # Try adding a new duplicate tags to a way
234   def test_new_duplicate_tags
235     # setup auth
236     basic_authorization(users(:normal_user).email, "test")
237
238     # create duplicate tag
239     tag_xml = XML::Node.new("tag")
240     tag_xml['k'] = "i_am_a_duplicate"
241     tag_xml['v'] = "foobar"
242
243     # add the tag into the existing xml
244     way_xml = current_ways(:visible_way).to_xml
245
246     # add two copies of the tag
247     way_xml.find("//osm/way").first << tag_xml.copy(true) << tag_xml
248
249     # try and upload it
250     content way_xml
251     put :update, :id => current_ways(:visible_way).id
252     assert_response :bad_request, 
253        "adding new duplicate tags to a way should fail with 'bad request'"
254   end
255
256   ##
257   # Try adding a new duplicate tags to a way.
258   # But be a bit subtle - use unicode decoding ambiguities to use different
259   # binary strings which have the same decoding.
260   #
261   # NOTE: I'm not sure this test is working correctly, as a lot of the tag
262   # keys seem to come out as "addr��housenumber". It might be something to
263   # do with Ruby's unicode handling...?
264   def test_invalid_duplicate_tags
265     # setup auth
266     basic_authorization(users(:normal_user).email, "test")
267
268     # add the tag into the existing xml
269     way_str = "<osm><way changeset='1'>"
270     way_str << "<tag k='addr:housenumber' v='1'/>"
271
272     # all of these keys have the same unicode decoding, but are binary
273     # not equal. libxml should make these identical as it decodes the
274     # XML document...
275     [ "addr\xc0\xbahousenumber",
276       "addr\xe0\x80\xbahousenumber",
277       "addr\xf0\x80\x80\xbahousenumber" ].each do |key|
278       # copy the XML doc to add the tags
279       way_str_copy = way_str.clone
280
281       # add all new tags to the way
282       way_str_copy << "<tag k='" << key << "' v='1'/>"
283       way_str_copy << "</way></osm>";
284
285       # try and upload it
286       content way_str_copy
287       put :create
288       assert_response :bad_request, 
289          "adding new duplicate tags to a way should fail with 'bad request'"
290     end
291   end
292
293   ##
294   # update the changeset_id of a node element
295   def update_changeset(xml, changeset_id)
296     xml_attr_rewrite(xml, 'changeset', changeset_id)
297   end
298
299   ##
300   # update an attribute in the node element
301   def xml_attr_rewrite(xml, name, value)
302     xml.find("//osm/way").first[name] = value.to_s
303     return xml
304   end
305 end