making it clearer when a test case fails, as to which test case it is that is failing...
[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   end
41
42   ##
43   # check the "full" mode
44   def test_full
45     get :full, :id => current_ways(:visible_way).id
46     assert_response :success
47     # FIXME check whether this contains the stuff we want!
48     #print @response.body
49     # Check the way is correctly returned
50     way = current_ways(:visible_way)
51     assert_select "osm way[id=#{way.id}][version=#{way.version}][visible=#{way.visible}]", 1
52     assert_select "osm way nd[ref=#{way.way_nodes[0].node_id}]", 1
53     # Check that the node is correctly returned
54     nd = current_ways(:visible_way).nodes
55     assert_equal 1, nd.count
56     nda = nd[0]
57     assert_select "osm node[id=#{nda.id}][version=#{nda.version}][lat=#{nda.lat}][lon=#{nda.lon}]", 1 
58   end
59
60   # -------------------------------------
61   # Test simple way creation.
62   # -------------------------------------
63
64   def test_create
65     nid1 = current_nodes(:used_node_1).id
66     nid2 = current_nodes(:used_node_2).id
67     basic_authorization "test@openstreetmap.org", "test"
68
69     # use the first user's open changeset
70     changeset_id = changesets(:normal_user_first_change).id
71     
72     # create a way with pre-existing nodes
73     content "<osm><way changeset='#{changeset_id}'>" +
74       "<nd ref='#{nid1}'/><nd ref='#{nid2}'/>" + 
75       "<tag k='test' v='yes' /></way></osm>"
76     put :create
77     # hope for success
78     assert_response :success, 
79         "way upload did not return success status"
80     # read id of created way and search for it
81     wayid = @response.body
82     checkway = Way.find(wayid)
83     assert_not_nil checkway, 
84         "uploaded way not found in data base after upload"
85     # compare values
86     assert_equal checkway.nds.length, 2, 
87         "saved way does not contain exactly one node"
88     assert_equal checkway.nds[0], nid1, 
89         "saved way does not contain the right node on pos 0"
90     assert_equal checkway.nds[1], nid2, 
91         "saved way does not contain the right node on pos 1"
92     assert_equal checkway.changeset_id, changeset_id,
93         "saved way does not belong to the correct changeset"
94     assert_equal users(:normal_user).id, checkway.changeset.user_id, 
95         "saved way does not belong to user that created it"
96     assert_equal true, checkway.visible, 
97         "saved way is not visible"
98   end
99
100   # -------------------------------------
101   # Test creating some invalid ways.
102   # -------------------------------------
103
104   def test_create_invalid
105     basic_authorization "test@openstreetmap.org", "test"
106
107     # use the first user's open changeset
108     open_changeset_id = changesets(:normal_user_first_change).id
109     closed_changeset_id = changesets(:normal_user_closed_change).id
110     nid1 = current_nodes(:used_node_1).id
111
112     # create a way with non-existing node
113     content "<osm><way changeset='#{open_changeset_id}'>" + 
114       "<nd ref='0'/><tag k='test' v='yes' /></way></osm>"
115     put :create
116     # expect failure
117     assert_response :precondition_failed, 
118         "way upload with invalid node did not return 'precondition failed'"
119
120     # create a way with no nodes
121     content "<osm><way changeset='#{open_changeset_id}'>" +
122       "<tag k='test' v='yes' /></way></osm>"
123     put :create
124     # expect failure
125     assert_response :precondition_failed, 
126         "way upload with no node did not return 'precondition failed'"
127
128     # create a way inside a closed changeset
129     content "<osm><way changeset='#{closed_changeset_id}'>" +
130       "<nd ref='#{nid1}'/></way></osm>"
131     put :create
132     # expect failure
133     assert_response :conflict, 
134         "way upload to closed changeset did not return 'conflict'"    
135   end
136
137   # -------------------------------------
138   # Test deleting ways.
139   # -------------------------------------
140   
141   def test_delete
142     # first try to delete way without auth
143     delete :delete, :id => current_ways(:visible_way).id
144     assert_response :unauthorized
145
146     # now set auth
147     basic_authorization("test@openstreetmap.org", "test");  
148
149     # this shouldn't work as with the 0.6 api we need pay load to delete
150     delete :delete, :id => current_ways(:visible_way).id
151     assert_response :bad_request
152     
153     # Now try without having a changeset
154     content "<osm><way id='#{current_ways(:visible_way).id}'></osm>"
155     delete :delete, :id => current_ways(:visible_way).id
156     assert_response :bad_request
157     
158     # try to delete with an invalid (closed) changeset
159     content update_changeset(current_ways(:visible_way).to_xml,
160                              changesets(:normal_user_closed_change).id)
161     delete :delete, :id => current_ways(:visible_way).id
162     assert_response :conflict
163
164     # try to delete with an invalid (non-existent) changeset
165     content update_changeset(current_ways(:visible_way).to_xml,0)
166     delete :delete, :id => current_ways(:visible_way).id
167     assert_response :conflict
168
169     # Now try with a valid changeset
170     content current_ways(:visible_way).to_xml
171     delete :delete, :id => current_ways(:visible_way).id
172     assert_response :success
173
174     # check the returned value - should be the new version number
175     # valid delete should return the new version number, which should
176     # be greater than the old version number
177     assert @response.body.to_i > current_ways(:visible_way).version,
178        "delete request should return a new version number for way"
179
180     # this won't work since the way is already deleted
181     content current_ways(:invisible_way).to_xml
182     delete :delete, :id => current_ways(:invisible_way).id
183     assert_response :gone
184
185     # this shouldn't work as the way is used in a relation
186     content current_ways(:used_way).to_xml
187     delete :delete, :id => current_ways(:used_way).id
188     assert_response :precondition_failed, 
189        "shouldn't be able to delete a way used in a relation (#{@response.body})"
190
191     # this won't work since the way never existed
192     delete :delete, :id => 0
193     assert_response :not_found
194   end
195
196   # ------------------------------------------------------------
197   # test tags handling
198   # ------------------------------------------------------------
199
200   ##
201   # Try adding a duplicate of an existing tag to a way
202   def test_add_duplicate_tags
203     # setup auth
204     basic_authorization(users(:normal_user).email, "test")
205
206     # add an identical tag to the way
207     tag_xml = XML::Node.new("tag")
208     tag_xml['k'] = current_way_tags(:t1).k
209     tag_xml['v'] = current_way_tags(:t1).v
210
211     # add the tag into the existing xml
212     way_xml = current_ways(:visible_way).to_xml
213     way_xml.find("//osm/way").first << tag_xml
214
215     # try and upload it
216     content way_xml
217     put :update, :id => current_ways(:visible_way).id
218     assert_response :bad_request, 
219        "adding a duplicate tag to a way should fail with 'bad request'"
220   end
221
222   ##
223   # Try adding a new duplicate tags to a way
224   def test_new_duplicate_tags
225     # setup auth
226     basic_authorization(users(:normal_user).email, "test")
227
228     # create duplicate tag
229     tag_xml = XML::Node.new("tag")
230     tag_xml['k'] = "i_am_a_duplicate"
231     tag_xml['v'] = "foobar"
232
233     # add the tag into the existing xml
234     way_xml = current_ways(:visible_way).to_xml
235
236     # add two copies of the tag
237     way_xml.find("//osm/way").first << tag_xml.copy(true) << tag_xml
238
239     # try and upload it
240     content way_xml
241     put :update, :id => current_ways(:visible_way).id
242     assert_response :bad_request, 
243        "adding new duplicate tags to a way should fail with 'bad request'"
244   end
245
246   ##
247   # Try adding a new duplicate tags to a way.
248   # But be a bit subtle - use unicode decoding ambiguities to use different
249   # binary strings which have the same decoding.
250   #
251   # NOTE: I'm not sure this test is working correctly, as a lot of the tag
252   # keys seem to come out as "addr��housenumber". It might be something to
253   # do with Ruby's unicode handling...?
254   def test_invalid_duplicate_tags
255     # setup auth
256     basic_authorization(users(:normal_user).email, "test")
257
258     # add the tag into the existing xml
259     way_str = "<osm><way changeset='1'>"
260     way_str << "<tag k='addr:housenumber' v='1'/>"
261
262     # all of these keys have the same unicode decoding, but are binary
263     # not equal. libxml should make these identical as it decodes the
264     # XML document...
265     [ "addr\xc0\xbahousenumber",
266       "addr\xe0\x80\xbahousenumber",
267       "addr\xf0\x80\x80\xbahousenumber" ].each do |key|
268       # copy the XML doc to add the tags
269       way_str_copy = way_str.clone
270
271       # add all new tags to the way
272       way_str_copy << "<tag k='" << key << "' v='1'/>"
273       way_str_copy << "</way></osm>";
274
275       # try and upload it
276       content way_str_copy
277       put :create
278       assert_response :bad_request, 
279          "adding new duplicate tags to a way should fail with 'bad request'"
280     end
281   end
282
283   ##
284   # test that a call to ways_for_node returns all ways that contain the node
285   # and none that don't.
286   def test_ways_for_node
287     # in current fixtures ways 1 and 3 all use node 3. ways 2 and 4 
288     # *used* to use it but doesn't.
289     get :ways_for_node, :id => current_nodes(:used_node_1).id
290     assert_response :success
291     ways_xml = XML::Parser.string(@response.body).parse
292     assert_not_nil ways_xml, "failed to parse ways_for_node response"
293
294     # check that the set of IDs match expectations
295     expected_way_ids = [ current_ways(:visible_way).id,
296                          current_ways(:used_way).id
297                        ]
298     found_way_ids = ways_xml.find("//osm/way").collect { |w| w["id"].to_i }
299     assert_equal expected_way_ids, found_way_ids,
300       "expected ways for node #{current_nodes(:used_node_1).id} did not match found"
301     
302     # check the full ways to ensure we're not missing anything
303     expected_way_ids.each do |id|
304       way_xml = ways_xml.find("//osm/way[@id=#{id}]").first
305       assert_ways_are_equal(Way.find(id),
306                             Way.from_xml_node(way_xml))
307     end
308   end
309
310   ##
311   # update the changeset_id of a node element
312   def update_changeset(xml, changeset_id)
313     xml_attr_rewrite(xml, 'changeset', changeset_id)
314   end
315
316   ##
317   # update an attribute in the node element
318   def xml_attr_rewrite(xml, name, value)
319     xml.find("//osm/way").first[name] = value.to_s
320     return xml
321   end
322 end