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