1 require File.dirname(__FILE__) + '/../test_helper'
 
   2 require 'way_controller'
 
   4 class WayControllerTest < ActionController::TestCase
 
   7   def basic_authorization(user, pass)
 
   8     @request.env["HTTP_AUTHORIZATION"] = "Basic %s" % Base64.encode64("#{user}:#{pass}")
 
  12     @request.env["RAW_POST_DATA"] = c.to_s
 
  15   # -------------------------------------
 
  17   # -------------------------------------
 
  20     # check that a visible way is returned properly
 
  21     get :read, :id => current_ways(:visible_way).id
 
  22     assert_response :success
 
  24     # check that an invisible way is not returned
 
  25     get :read, :id => current_ways(:invisible_way).id
 
  28     # check chat a non-existent way is not returned
 
  30     assert_response :not_found
 
  34   # check the "full" mode
 
  36     Way.find(:all).each do |way|
 
  37       get :full, :id => way.id
 
  39       # full call should say "gone" for non-visible ways...
 
  45       # otherwise it should say success
 
  46       assert_response :success
 
  48       # Check the way is correctly returned
 
  49       assert_select "osm way[id=#{way.id}][version=#{way.version}][visible=#{way.visible}]", 1
 
  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.
 
  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
 
  62   # -------------------------------------
 
  63   # Test simple way creation.
 
  64   # -------------------------------------
 
  67     nid1 = current_nodes(:used_node_1).id
 
  68     nid2 = current_nodes(:used_node_2).id
 
  69     basic_authorization "test@openstreetmap.org", "test"
 
  71     # use the first user's open changeset
 
  72     changeset_id = changesets(:normal_user_first_change).id
 
  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>"
 
  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"
 
  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"
 
 102   # -------------------------------------
 
 103   # Test creating some invalid ways.
 
 104   # -------------------------------------
 
 106   def test_create_invalid
 
 107     basic_authorization "test@openstreetmap.org", "test"
 
 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
 
 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>"
 
 119     assert_response :precondition_failed, 
 
 120         "way upload with invalid node did not return 'precondition failed'"
 
 122     # create a way with no nodes
 
 123     content "<osm><way changeset='#{open_changeset_id}'>" +
 
 124       "<tag k='test' v='yes' /></way></osm>"
 
 127     assert_response :precondition_failed, 
 
 128         "way upload with no node did not return 'precondition failed'"
 
 130     # create a way inside a closed changeset
 
 131     content "<osm><way changeset='#{closed_changeset_id}'>" +
 
 132       "<nd ref='#{nid1}'/></way></osm>"
 
 135     assert_response :conflict, 
 
 136         "way upload to closed changeset did not return 'conflict'"    
 
 139   # -------------------------------------
 
 140   # Test deleting ways.
 
 141   # -------------------------------------
 
 144     # first try to delete way without auth
 
 145     delete :delete, :id => current_ways(:visible_way).id
 
 146     assert_response :unauthorized
 
 149     basic_authorization("test@openstreetmap.org", "test");  
 
 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
 
 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
 
 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
 
 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
 
 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
 
 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"
 
 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
 
 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})"
 
 193     # this won't work since the way never existed
 
 194     delete :delete, :id => 0
 
 195     assert_response :not_found
 
 198   # ------------------------------------------------------------
 
 200   # ------------------------------------------------------------
 
 203   # Try adding a duplicate of an existing tag to a way
 
 204   def test_add_duplicate_tags
 
 206     basic_authorization(users(:normal_user).email, "test")
 
 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
 
 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
 
 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'"
 
 225   # Try adding a new duplicate tags to a way
 
 226   def test_new_duplicate_tags
 
 228     basic_authorization(users(:normal_user).email, "test")
 
 230     # create duplicate tag
 
 231     tag_xml = XML::Node.new("tag")
 
 232     tag_xml['k'] = "i_am_a_duplicate"
 
 233     tag_xml['v'] = "foobar"
 
 235     # add the tag into the existing xml
 
 236     way_xml = current_ways(:visible_way).to_xml
 
 238     # add two copies of the tag
 
 239     way_xml.find("//osm/way").first << tag_xml.copy(true) << tag_xml
 
 243     put :update, :id => current_ways(:visible_way).id
 
 244     assert_response :bad_request, 
 
 245        "adding new duplicate tags to a way should fail with 'bad request'"
 
 249   # Try adding a new duplicate tags to a way.
 
 250   # But be a bit subtle - use unicode decoding ambiguities to use different
 
 251   # binary strings which have the same decoding.
 
 253   # NOTE: I'm not sure this test is working correctly, as a lot of the tag
 
 254   # keys seem to come out as "addr��housenumber". It might be something to
 
 255   # do with Ruby's unicode handling...?
 
 256   def test_invalid_duplicate_tags
 
 258     basic_authorization(users(:normal_user).email, "test")
 
 260     # add the tag into the existing xml
 
 261     way_str = "<osm><way changeset='1'>"
 
 262     way_str << "<tag k='addr:housenumber' v='1'/>"
 
 264     # all of these keys have the same unicode decoding, but are binary
 
 265     # not equal. libxml should make these identical as it decodes the
 
 267     [ "addr\xc0\xbahousenumber",
 
 268       "addr\xe0\x80\xbahousenumber",
 
 269       "addr\xf0\x80\x80\xbahousenumber" ].each do |key|
 
 270       # copy the XML doc to add the tags
 
 271       way_str_copy = way_str.clone
 
 273       # add all new tags to the way
 
 274       way_str_copy << "<tag k='" << key << "' v='1'/>"
 
 275       way_str_copy << "</way></osm>";
 
 280       assert_response :bad_request, 
 
 281          "adding new duplicate tags to a way should fail with 'bad request'"
 
 286   # test that a call to ways_for_node returns all ways that contain the node
 
 287   # and none that don't.
 
 288   def test_ways_for_node
 
 289     # in current fixtures ways 1 and 3 all use node 3. ways 2 and 4 
 
 290     # *used* to use it but doesn't.
 
 291     get :ways_for_node, :id => current_nodes(:used_node_1).id
 
 292     assert_response :success
 
 293     ways_xml = XML::Parser.string(@response.body).parse
 
 294     assert_not_nil ways_xml, "failed to parse ways_for_node response"
 
 296     # check that the set of IDs match expectations
 
 297     expected_way_ids = [ current_ways(:visible_way).id,
 
 298                          current_ways(:used_way).id
 
 300     found_way_ids = ways_xml.find("//osm/way").collect { |w| w["id"].to_i }
 
 301     assert_equal expected_way_ids, found_way_ids,
 
 302       "expected ways for node #{current_nodes(:used_node_1).id} did not match found"
 
 304     # check the full ways to ensure we're not missing anything
 
 305     expected_way_ids.each do |id|
 
 306       way_xml = ways_xml.find("//osm/way[@id=#{id}]").first
 
 307       assert_ways_are_equal(Way.find(id),
 
 308                             Way.from_xml_node(way_xml))
 
 313   # update the changeset_id of a node element
 
 314   def update_changeset(xml, changeset_id)
 
 315     xml_attr_rewrite(xml, 'changeset', changeset_id)
 
 319   # update an attribute in the node element
 
 320   def xml_attr_rewrite(xml, name, value)
 
 321     xml.find("//osm/way").first[name] = value.to_s