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