diff --git a/app/abilities/ability.rb b/app/abilities/ability.rb index 4127c0de79..5e8ef997fb 100644 --- a/app/abilities/ability.rb +++ b/app/abilities/ability.rb @@ -4,9 +4,10 @@ class Ability include CanCan::Ability def initialize(user) - can [:trackpoints, :map, :changes, :capabilities, :permissions], :api can [:relation, :relation_history, :way, :way_history, :node, :node_history, :changeset, :note, :new_note, :query], :browse + can :show, :capability + can :index, :change can [:index, :feed, :show, :download, :query], Changeset can :index, ChangesetComment can :search, :direction @@ -15,12 +16,15 @@ def initialize(user) can [:finish, :embed], :export can [:search, :search_latlon, :search_ca_postcode, :search_osm_nominatim, :search_geonames, :search_osm_nominatim_reverse, :search_geonames_reverse], :geocoder + can :index, :map can [:index, :create, :comment, :feed, :show, :search, :mine], Note can [:token, :request_token, :access_token, :test_request], :oauth + can :show, :permission can [:index, :show], Redaction can [:search_all, :search_nodes, :search_ways, :search_relations], :search can [:trackpoints], :swf can [:index, :show, :data, :georss, :picture, :icon], Trace + can :index, Tracepoint can [:terms, :api_users, :login, :logout, :new, :create, :save, :confirm, :confirm_resend, :confirm_email, :lost_password, :reset_password, :show, :api_read, :auth_success, :auth_failure], User can [:index, :show, :blocks_on, :blocks_by], UserBlock can [:index, :show], Node diff --git a/app/controllers/api/capabilities_controller.rb b/app/controllers/api/capabilities_controller.rb new file mode 100644 index 0000000000..8337bc8091 --- /dev/null +++ b/app/controllers/api/capabilities_controller.rb @@ -0,0 +1,21 @@ +module Api + class CapabilitiesController < ApplicationController + skip_before_action :verify_authenticity_token + before_action :api_deny_access_handler + + authorize_resource :class => false + + around_action :api_call_handle_error, :api_call_timeout + + # External apps that use the api are able to query the api to find out some + # parameters of the API. It currently returns: + # * minimum and maximum API versions that can be used. + # * maximum area that can be requested in a bbox request in square degrees + # * number of tracepoints that are returned in each tracepoints page + def show + @database_status = database_status + @api_status = api_status + @gpx_status = gpx_status + end + end +end diff --git a/app/controllers/api/changes_controller.rb b/app/controllers/api/changes_controller.rb new file mode 100644 index 0000000000..c9195e1d9b --- /dev/null +++ b/app/controllers/api/changes_controller.rb @@ -0,0 +1,57 @@ +module Api + class ChangesController < ApplicationController + skip_before_action :verify_authenticity_token + before_action :api_deny_access_handler + + authorize_resource :class => false + + before_action :check_api_readable + around_action :api_call_handle_error, :api_call_timeout + + # Get a list of the tiles that have changed within a specified time + # period + def index + zoom = (params[:zoom] || "12").to_i + + if params.include?(:start) && params.include?(:end) + starttime = Time.parse(params[:start]) + endtime = Time.parse(params[:end]) + else + hours = (params[:hours] || "1").to_i.hours + endtime = Time.now.getutc + starttime = endtime - hours + end + + if zoom >= 1 && zoom <= 16 && + endtime > starttime && endtime - starttime <= 24.hours + mask = (1 << zoom) - 1 + + tiles = Node.where(:timestamp => starttime..endtime).group("maptile_for_point(latitude, longitude, #{zoom})").count + + doc = OSM::API.new.get_xml_doc + changes = XML::Node.new "changes" + changes["starttime"] = starttime.xmlschema + changes["endtime"] = endtime.xmlschema + + tiles.each do |tile, count| + x = (tile.to_i >> zoom) & mask + y = tile.to_i & mask + + t = XML::Node.new "tile" + t["x"] = x.to_s + t["y"] = y.to_s + t["z"] = zoom.to_s + t["changes"] = count.to_s + + changes << t + end + + doc.root << changes + + render :xml => doc.to_s + else + render :plain => "Requested zoom is invalid, or the supplied start is after the end time, or the start duration is more than 24 hours", :status => :bad_request + end + end + end +end diff --git a/app/controllers/api/map_controller.rb b/app/controllers/api/map_controller.rb new file mode 100644 index 0000000000..47c0aeb36a --- /dev/null +++ b/app/controllers/api/map_controller.rb @@ -0,0 +1,107 @@ +module Api + class MapController < ApplicationController + skip_before_action :verify_authenticity_token + before_action :api_deny_access_handler + + authorize_resource :class => false + + before_action :check_api_readable + around_action :api_call_handle_error, :api_call_timeout + + # This is probably the most common call of all. It is used for getting the + # OSM data for a specified bounding box, usually for editing. First the + # bounding box (bbox) is checked to make sure that it is sane. All nodes + # are searched, then all the ways that reference those nodes are found. + # All Nodes that are referenced by those ways are fetched and added to the list + # of nodes. + # Then all the relations that reference the already found nodes and ways are + # fetched. All the nodes and ways that are referenced by those ways are then + # fetched. Finally all the xml is returned. + def index + # Figure out the bbox + # check boundary is sane and area within defined + # see /config/application.yml + begin + bbox = BoundingBox.from_bbox_params(params) + bbox.check_boundaries + bbox.check_size + rescue StandardError => err + report_error(err.message) + return + end + + nodes = Node.bbox(bbox).where(:visible => true).includes(:node_tags).limit(MAX_NUMBER_OF_NODES + 1) + + node_ids = nodes.collect(&:id) + if node_ids.length > MAX_NUMBER_OF_NODES + report_error("You requested too many nodes (limit is #{MAX_NUMBER_OF_NODES}). Either request a smaller area, or use planet.osm") + return + end + + doc = OSM::API.new.get_xml_doc + + # add bounds + doc.root << bbox.add_bounds_to(XML::Node.new("bounds")) + + # get ways + # find which ways are needed + ways = [] + if node_ids.empty? + list_of_way_nodes = [] + else + way_nodes = WayNode.where(:node_id => node_ids) + way_ids = way_nodes.collect { |way_node| way_node.id[0] } + ways = Way.preload(:way_nodes, :way_tags).find(way_ids) + + list_of_way_nodes = ways.collect do |way| + way.way_nodes.collect(&:node_id) + end + list_of_way_nodes.flatten! + end + + # - [0] in case some thing links to node 0 which doesn't exist. Shouldn't actually ever happen but it does. FIXME: file a ticket for this + nodes_to_fetch = (list_of_way_nodes.uniq - node_ids) - [0] + + nodes += Node.includes(:node_tags).find(nodes_to_fetch) unless nodes_to_fetch.empty? + + visible_nodes = {} + changeset_cache = {} + user_display_name_cache = {} + + nodes.each do |node| + if node.visible? + doc.root << node.to_xml_node(changeset_cache, user_display_name_cache) + visible_nodes[node.id] = node + end + end + + way_ids = [] + ways.each do |way| + if way.visible? + doc.root << way.to_xml_node(visible_nodes, changeset_cache, user_display_name_cache) + way_ids << way.id + end + end + + relations = Relation.nodes(visible_nodes.keys).visible + + Relation.ways(way_ids).visible + + # we do not normally return the "other" partners referenced by an relation, + # e.g. if we return a way A that is referenced by relation X, and there's + # another way B also referenced, that is not returned. But we do make + # an exception for cases where an relation references another *relation*; + # in that case we return that as well (but we don't go recursive here) + relations += Relation.relations(relations.collect(&:id)).visible + + # this "uniq" may be slightly inefficient; it may be better to first collect and output + # all node-related relations, then find the *not yet covered* way-related ones etc. + relations.uniq.each do |relation| + doc.root << relation.to_xml_node(changeset_cache, user_display_name_cache) + end + + response.headers["Content-Disposition"] = "attachment; filename=\"map.osm\"" + + render :xml => doc.to_s + end + end +end diff --git a/app/controllers/api/permissions_controller.rb b/app/controllers/api/permissions_controller.rb new file mode 100644 index 0000000000..b24aca776d --- /dev/null +++ b/app/controllers/api/permissions_controller.rb @@ -0,0 +1,27 @@ +module Api + class PermissionsController < ApplicationController + skip_before_action :verify_authenticity_token + before_action :api_deny_access_handler + + authorize_resource :class => false + + before_action :check_api_readable + before_action :setup_user_auth + around_action :api_call_handle_error, :api_call_timeout + + # External apps that use the api are able to query which permissions + # they have. This currently returns a list of permissions granted to the current user: + # * if authenticated via OAuth, this list will contain all permissions granted by the user to the access_token. + # * if authenticated via basic auth all permissions are granted, so the list will contain all permissions. + # * unauthenticated users have no permissions, so the list will be empty. + def show + @permissions = if current_token.present? + ClientApplication.all_permissions.select { |p| current_token.read_attribute(p) } + elsif current_user + ClientApplication.all_permissions + else + [] + end + end + end +end diff --git a/app/controllers/api/tracepoints_controller.rb b/app/controllers/api/tracepoints_controller.rb new file mode 100644 index 0000000000..56cd36138b --- /dev/null +++ b/app/controllers/api/tracepoints_controller.rb @@ -0,0 +1,112 @@ +module Api + class TracepointsController < ApplicationController + skip_before_action :verify_authenticity_token + before_action :api_deny_access_handler + + authorize_resource + + before_action :check_api_readable + around_action :api_call_handle_error, :api_call_timeout + + # Get an XML response containing a list of tracepoints that have been uploaded + # within the specified bounding box, and in the specified page. + def index + # retrieve the page number + page = params["page"].to_s.to_i + + unless page >= 0 + report_error("Page number must be greater than or equal to 0") + return + end + + offset = page * TRACEPOINTS_PER_PAGE + + # Figure out the bbox + # check boundary is sane and area within defined + # see /config/application.yml + begin + bbox = BoundingBox.from_bbox_params(params) + bbox.check_boundaries + bbox.check_size + rescue StandardError => err + report_error(err.message) + return + end + + # get all the points + ordered_points = Tracepoint.bbox(bbox).joins(:trace).where(:gpx_files => { :visibility => %w[trackable identifiable] }).order("gpx_id DESC, trackid ASC, timestamp ASC") + unordered_points = Tracepoint.bbox(bbox).joins(:trace).where(:gpx_files => { :visibility => %w[public private] }).order("gps_points.latitude", "gps_points.longitude", "gps_points.timestamp") + points = ordered_points.union_all(unordered_points).offset(offset).limit(TRACEPOINTS_PER_PAGE) + + doc = XML::Document.new + doc.encoding = XML::Encoding::UTF_8 + root = XML::Node.new "gpx" + root["version"] = "1.0" + root["creator"] = "OpenStreetMap.org" + root["xmlns"] = "http://www.topografix.com/GPX/1/0" + + doc.root = root + + # initialise these variables outside of the loop so that they + # stay in scope and don't get free'd up by the GC during the + # loop. + gpx_id = -1 + trackid = -1 + track = nil + trkseg = nil + anon_track = nil + anon_trkseg = nil + gpx_file = nil + timestamps = false + + points.each do |point| + if gpx_id != point.gpx_id + gpx_id = point.gpx_id + trackid = -1 + gpx_file = Trace.find(gpx_id) + + if gpx_file.trackable? + track = XML::Node.new "trk" + doc.root << track + timestamps = true + + if gpx_file.identifiable? + track << (XML::Node.new("name") << gpx_file.name) + track << (XML::Node.new("desc") << gpx_file.description) + track << (XML::Node.new("url") << url_for(:controller => "/traces", :action => "show", :display_name => gpx_file.user.display_name, :id => gpx_file.id)) + end + else + # use the anonymous track segment if the user hasn't allowed + # their GPX points to be tracked. + timestamps = false + if anon_track.nil? + anon_track = XML::Node.new "trk" + doc.root << anon_track + end + track = anon_track + end + end + + if trackid != point.trackid + if gpx_file.trackable? + trkseg = XML::Node.new "trkseg" + track << trkseg + trackid = point.trackid + else + if anon_trkseg.nil? + anon_trkseg = XML::Node.new "trkseg" + anon_track << anon_trkseg + end + trkseg = anon_trkseg + end + end + + trkseg << point.to_xml_node(timestamps) + end + + response.headers["Content-Disposition"] = "attachment; filename=\"tracks.gpx\"" + + render :xml => doc.to_s + end + end +end diff --git a/app/controllers/api_controller.rb b/app/controllers/api_controller.rb deleted file mode 100644 index 3273665d23..0000000000 --- a/app/controllers/api_controller.rb +++ /dev/null @@ -1,279 +0,0 @@ -class ApiController < ApplicationController - skip_before_action :verify_authenticity_token - before_action :api_deny_access_handler - - authorize_resource :class => false - - before_action :check_api_readable, :except => [:capabilities] - before_action :setup_user_auth, :only => [:permissions] - around_action :api_call_handle_error, :api_call_timeout - - # Get an XML response containing a list of tracepoints that have been uploaded - # within the specified bounding box, and in the specified page. - def trackpoints - # retrieve the page number - page = params["page"].to_s.to_i - - unless page >= 0 - report_error("Page number must be greater than or equal to 0") - return - end - - offset = page * TRACEPOINTS_PER_PAGE - - # Figure out the bbox - # check boundary is sane and area within defined - # see /config/application.yml - begin - bbox = BoundingBox.from_bbox_params(params) - bbox.check_boundaries - bbox.check_size - rescue StandardError => err - report_error(err.message) - return - end - - # get all the points - ordered_points = Tracepoint.bbox(bbox).joins(:trace).where(:gpx_files => { :visibility => %w[trackable identifiable] }).order("gpx_id DESC, trackid ASC, timestamp ASC") - unordered_points = Tracepoint.bbox(bbox).joins(:trace).where(:gpx_files => { :visibility => %w[public private] }).order("gps_points.latitude", "gps_points.longitude", "gps_points.timestamp") - points = ordered_points.union_all(unordered_points).offset(offset).limit(TRACEPOINTS_PER_PAGE) - - doc = XML::Document.new - doc.encoding = XML::Encoding::UTF_8 - root = XML::Node.new "gpx" - root["version"] = "1.0" - root["creator"] = "OpenStreetMap.org" - root["xmlns"] = "http://www.topografix.com/GPX/1/0" - - doc.root = root - - # initialise these variables outside of the loop so that they - # stay in scope and don't get free'd up by the GC during the - # loop. - gpx_id = -1 - trackid = -1 - track = nil - trkseg = nil - anon_track = nil - anon_trkseg = nil - gpx_file = nil - timestamps = false - - points.each do |point| - if gpx_id != point.gpx_id - gpx_id = point.gpx_id - trackid = -1 - gpx_file = Trace.find(gpx_id) - - if gpx_file.trackable? - track = XML::Node.new "trk" - doc.root << track - timestamps = true - - if gpx_file.identifiable? - track << (XML::Node.new("name") << gpx_file.name) - track << (XML::Node.new("desc") << gpx_file.description) - track << (XML::Node.new("url") << url_for(:controller => "traces", :action => "show", :display_name => gpx_file.user.display_name, :id => gpx_file.id)) - end - else - # use the anonymous track segment if the user hasn't allowed - # their GPX points to be tracked. - timestamps = false - if anon_track.nil? - anon_track = XML::Node.new "trk" - doc.root << anon_track - end - track = anon_track - end - end - - if trackid != point.trackid - if gpx_file.trackable? - trkseg = XML::Node.new "trkseg" - track << trkseg - trackid = point.trackid - else - if anon_trkseg.nil? - anon_trkseg = XML::Node.new "trkseg" - anon_track << anon_trkseg - end - trkseg = anon_trkseg - end - end - - trkseg << point.to_xml_node(timestamps) - end - - response.headers["Content-Disposition"] = "attachment; filename=\"tracks.gpx\"" - - render :xml => doc.to_s - end - - # This is probably the most common call of all. It is used for getting the - # OSM data for a specified bounding box, usually for editing. First the - # bounding box (bbox) is checked to make sure that it is sane. All nodes - # are searched, then all the ways that reference those nodes are found. - # All Nodes that are referenced by those ways are fetched and added to the list - # of nodes. - # Then all the relations that reference the already found nodes and ways are - # fetched. All the nodes and ways that are referenced by those ways are then - # fetched. Finally all the xml is returned. - def map - # Figure out the bbox - # check boundary is sane and area within defined - # see /config/application.yml - begin - bbox = BoundingBox.from_bbox_params(params) - bbox.check_boundaries - bbox.check_size - rescue StandardError => err - report_error(err.message) - return - end - - nodes = Node.bbox(bbox).where(:visible => true).includes(:node_tags).limit(MAX_NUMBER_OF_NODES + 1) - - node_ids = nodes.collect(&:id) - if node_ids.length > MAX_NUMBER_OF_NODES - report_error("You requested too many nodes (limit is #{MAX_NUMBER_OF_NODES}). Either request a smaller area, or use planet.osm") - return - end - - doc = OSM::API.new.get_xml_doc - - # add bounds - doc.root << bbox.add_bounds_to(XML::Node.new("bounds")) - - # get ways - # find which ways are needed - ways = [] - if node_ids.empty? - list_of_way_nodes = [] - else - way_nodes = WayNode.where(:node_id => node_ids) - way_ids = way_nodes.collect { |way_node| way_node.id[0] } - ways = Way.preload(:way_nodes, :way_tags).find(way_ids) - - list_of_way_nodes = ways.collect do |way| - way.way_nodes.collect(&:node_id) - end - list_of_way_nodes.flatten! - end - - # - [0] in case some thing links to node 0 which doesn't exist. Shouldn't actually ever happen but it does. FIXME: file a ticket for this - nodes_to_fetch = (list_of_way_nodes.uniq - node_ids) - [0] - - nodes += Node.includes(:node_tags).find(nodes_to_fetch) unless nodes_to_fetch.empty? - - visible_nodes = {} - changeset_cache = {} - user_display_name_cache = {} - - nodes.each do |node| - if node.visible? - doc.root << node.to_xml_node(changeset_cache, user_display_name_cache) - visible_nodes[node.id] = node - end - end - - way_ids = [] - ways.each do |way| - if way.visible? - doc.root << way.to_xml_node(visible_nodes, changeset_cache, user_display_name_cache) - way_ids << way.id - end - end - - relations = Relation.nodes(visible_nodes.keys).visible + - Relation.ways(way_ids).visible - - # we do not normally return the "other" partners referenced by an relation, - # e.g. if we return a way A that is referenced by relation X, and there's - # another way B also referenced, that is not returned. But we do make - # an exception for cases where an relation references another *relation*; - # in that case we return that as well (but we don't go recursive here) - relations += Relation.relations(relations.collect(&:id)).visible - - # this "uniq" may be slightly inefficient; it may be better to first collect and output - # all node-related relations, then find the *not yet covered* way-related ones etc. - relations.uniq.each do |relation| - doc.root << relation.to_xml_node(changeset_cache, user_display_name_cache) - end - - response.headers["Content-Disposition"] = "attachment; filename=\"map.osm\"" - - render :xml => doc.to_s - end - - # Get a list of the tiles that have changed within a specified time - # period - def changes - zoom = (params[:zoom] || "12").to_i - - if params.include?(:start) && params.include?(:end) - starttime = Time.parse(params[:start]) - endtime = Time.parse(params[:end]) - else - hours = (params[:hours] || "1").to_i.hours - endtime = Time.now.getutc - starttime = endtime - hours - end - - if zoom >= 1 && zoom <= 16 && - endtime > starttime && endtime - starttime <= 24.hours - mask = (1 << zoom) - 1 - - tiles = Node.where(:timestamp => starttime..endtime).group("maptile_for_point(latitude, longitude, #{zoom})").count - - doc = OSM::API.new.get_xml_doc - changes = XML::Node.new "changes" - changes["starttime"] = starttime.xmlschema - changes["endtime"] = endtime.xmlschema - - tiles.each do |tile, count| - x = (tile.to_i >> zoom) & mask - y = tile.to_i & mask - - t = XML::Node.new "tile" - t["x"] = x.to_s - t["y"] = y.to_s - t["z"] = zoom.to_s - t["changes"] = count.to_s - - changes << t - end - - doc.root << changes - - render :xml => doc.to_s - else - render :plain => "Requested zoom is invalid, or the supplied start is after the end time, or the start duration is more than 24 hours", :status => :bad_request - end - end - - # External apps that use the api are able to query the api to find out some - # parameters of the API. It currently returns: - # * minimum and maximum API versions that can be used. - # * maximum area that can be requested in a bbox request in square degrees - # * number of tracepoints that are returned in each tracepoints page - def capabilities - @database_status = database_status - @api_status = api_status - @gpx_status = gpx_status - end - - # External apps that use the api are able to query which permissions - # they have. This currently returns a list of permissions granted to the current user: - # * if authenticated via OAuth, this list will contain all permissions granted by the user to the access_token. - # * if authenticated via basic auth all permissions are granted, so the list will contain all permissions. - # * unauthenticated users have no permissions, so the list will be empty. - def permissions - @permissions = if current_token.present? - ClientApplication.all_permissions.select { |p| current_token.read_attribute(p) } - elsif current_user - ClientApplication.all_permissions - else - [] - end - end -end diff --git a/app/controllers/export_controller.rb b/app/controllers/export_controller.rb index 18ac15c101..fc5b0ec80e 100644 --- a/app/controllers/export_controller.rb +++ b/app/controllers/export_controller.rb @@ -13,7 +13,7 @@ def finish if format == "osm" # redirect to API map get - redirect_to :controller => "api", :action => "map", :bbox => bbox + redirect_to :controller => "api/map", :action => "index", :bbox => bbox elsif format == "mapnik" # redirect to a special 'export' cgi script diff --git a/app/views/api/capabilities.builder b/app/views/api/capabilities/show.builder similarity index 100% rename from app/views/api/capabilities.builder rename to app/views/api/capabilities/show.builder diff --git a/app/views/api/permissions.builder b/app/views/api/permissions/show.builder similarity index 100% rename from app/views/api/permissions.builder rename to app/views/api/permissions/show.builder diff --git a/config/routes.rb b/config/routes.rb index b0fe5e48e8..3c0cf61f15 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,10 +1,12 @@ OpenStreetMap::Application.routes.draw do # API - get "api/capabilities" => "api#capabilities" + namespace :api do + get "capabilities" => "capabilities#show" + end scope "api/0.6" do - get "capabilities" => "api#capabilities" - get "permissions" => "api#permissions" + get "capabilities" => "api/capabilities#show" + get "permissions" => "api/permissions#show" put "changeset/create" => "changesets#create" post "changeset/:id/upload" => "changesets#upload", :id => /\d+/ @@ -53,11 +55,11 @@ delete "relation/:id" => "relations#delete", :id => /\d+/ get "relations" => "relations#index" - get "map" => "api#map" + get "map" => "api/map#index" - get "trackpoints" => "api#trackpoints" + get "trackpoints" => "api/tracepoints#index" - get "changes" => "api#changes" + get "changes" => "api/changes#index" get "search" => "search#search_all", :as => "api_search" get "ways/search" => "search#search_ways" diff --git a/test/controllers/api/capabilities_controller_test.rb b/test/controllers/api/capabilities_controller_test.rb new file mode 100644 index 0000000000..127201f9c2 --- /dev/null +++ b/test/controllers/api/capabilities_controller_test.rb @@ -0,0 +1,35 @@ +require "test_helper" + +module Api + class CapabilitiesControllerTest < ActionController::TestCase + ## + # test all routes which lead to this controller + def test_routes + assert_routing( + { :path => "/api/capabilities", :method => :get }, + { :controller => "api/capabilities", :action => "show" } + ) + assert_recognizes( + { :controller => "api/capabilities", :action => "show" }, + { :path => "/api/0.6/capabilities", :method => :get } + ) + end + + def test_capabilities + get :show + assert_response :success + assert_select "osm[version='#{API_VERSION}'][generator='#{GENERATOR}']", :count => 1 do + assert_select "api", :count => 1 do + assert_select "version[minimum='#{API_VERSION}'][maximum='#{API_VERSION}']", :count => 1 + assert_select "area[maximum='#{MAX_REQUEST_AREA}']", :count => 1 + assert_select "note_area[maximum='#{MAX_NOTE_REQUEST_AREA}']", :count => 1 + assert_select "tracepoints[per_page='#{TRACEPOINTS_PER_PAGE}']", :count => 1 + assert_select "changesets[maximum_elements='#{Changeset::MAX_ELEMENTS}']", :count => 1 + assert_select "status[database='online']", :count => 1 + assert_select "status[api='online']", :count => 1 + assert_select "status[gpx='online']", :count => 1 + end + end + end + end +end diff --git a/test/controllers/api/changes_controller_test.rb b/test/controllers/api/changes_controller_test.rb new file mode 100644 index 0000000000..c212ba1d07 --- /dev/null +++ b/test/controllers/api/changes_controller_test.rb @@ -0,0 +1,106 @@ +require "test_helper" + +module Api + class ChangesControllerTest < ActionController::TestCase + ## + # test all routes which lead to this controller + def test_routes + assert_routing( + { :path => "/api/0.6/changes", :method => :get }, + { :controller => "api/changes", :action => "index" } + ) + end + + # MySQL and Postgres require that the C based functions are installed for + # this test to work. More information is available from: + # http://wiki.openstreetmap.org/wiki/Rails#Installing_the_quadtile_functions + # or by looking at the readme in db/README + def test_changes_simple + # create a selection of nodes + (1..5).each do |n| + create(:node, :timestamp => Time.utc(2007, 1, 1, 0, 0, 0), :lat => n, :lon => n) + end + # deleted nodes should also be counted + create(:node, :deleted, :timestamp => Time.utc(2007, 1, 1, 0, 0, 0), :lat => 6, :lon => 6) + # nodes in the same tile won't change the total + create(:node, :timestamp => Time.utc(2007, 1, 1, 0, 0, 0), :lat => 6, :lon => 6) + # nodes with a different timestamp should be ignored + create(:node, :timestamp => Time.utc(2008, 1, 1, 0, 0, 0), :lat => 7, :lon => 7) + + travel_to Time.utc(2010, 4, 3, 10, 55, 0) do + get :index + assert_response :success + now = Time.now.getutc + hourago = now - 1.hour + assert_select "osm[version='#{API_VERSION}'][generator='#{GENERATOR}']", :count => 1 do + assert_select "changes[starttime='#{hourago.xmlschema}'][endtime='#{now.xmlschema}']", :count => 1 do + assert_select "tile", :count => 0 + end + end + end + + travel_to Time.utc(2007, 1, 1, 0, 30, 0) do + get :index + assert_response :success + # print @response.body + # As we have loaded the fixtures, we can assume that there are some + # changes at the time we have frozen at + now = Time.now.getutc + hourago = now - 1.hour + assert_select "osm[version='#{API_VERSION}'][generator='#{GENERATOR}']", :count => 1 do + assert_select "changes[starttime='#{hourago.xmlschema}'][endtime='#{now.xmlschema}']", :count => 1 do + assert_select "tile", :count => 6 + end + end + end + end + + def test_changes_zoom_invalid + zoom_to_test = %w[p -1 0 17 one two] + zoom_to_test.each do |zoom| + get :index, :params => { :zoom => zoom } + assert_response :bad_request + assert_equal @response.body, "Requested zoom is invalid, or the supplied start is after the end time, or the start duration is more than 24 hours" + end + end + + def test_changes_zoom_valid + 1.upto(16) do |zoom| + get :index, :params => { :zoom => zoom } + assert_response :success + # NOTE: there was a test here for the timing, but it was too sensitive to be a good test + # and it was annoying. + assert_select "osm[version='#{API_VERSION}'][generator='#{GENERATOR}']", :count => 1 do + assert_select "changes", :count => 1 + end + end + end + + def test_changes_hours_invalid + invalid = %w[-21 335 -1 0 25 26 100 one two three ping pong :] + invalid.each do |hour| + get :index, :params => { :hours => hour } + assert_response :bad_request, "Problem with the hour: #{hour}" + assert_equal @response.body, "Requested zoom is invalid, or the supplied start is after the end time, or the start duration is more than 24 hours", "Problem with the hour: #{hour}." + end + end + + def test_changes_hours_valid + 1.upto(24) do |hour| + get :index, :params => { :hours => hour } + assert_response :success + end + end + + def test_changes_start_end_invalid + get :index, :params => { :start => "2010-04-03 10:55:00", :end => "2010-04-03 09:55:00" } + assert_response :bad_request + assert_equal @response.body, "Requested zoom is invalid, or the supplied start is after the end time, or the start duration is more than 24 hours" + end + + def test_changes_start_end_valid + get :index, :params => { :start => "2010-04-03 09:55:00", :end => "2010-04-03 10:55:00" } + assert_response :success + end + end +end diff --git a/test/controllers/api/map_controller_test.rb b/test/controllers/api/map_controller_test.rb new file mode 100644 index 0000000000..5ee5a9e6cc --- /dev/null +++ b/test/controllers/api/map_controller_test.rb @@ -0,0 +1,181 @@ +require "test_helper" + +module Api + class MapControllerTest < ActionController::TestCase + def setup + super + @badbigbbox = %w[-0.1,-0.1,1.1,1.1 10,10,11,11] + @badmalformedbbox = %w[-0.1 hello + 10N2W10.1N2.1W] + @badlatmixedbbox = %w[0,0.1,0.1,0 -0.1,80,0.1,70 0.24,54.34,0.25,54.33] + @badlonmixedbbox = %w[80,-0.1,70,0.1 54.34,0.24,54.33,0.25] + # @badlatlonoutboundsbbox = %w{ 191,-0.1,193,0.1 -190.1,89.9,-190,90 } + @goodbbox = %w[-0.1,-0.1,0.1,0.1 51.1,-0.1,51.2,0 + -0.1,%20-0.1,%200.1,%200.1 -0.1edcd,-0.1d,0.1,0.1 -0.1E,-0.1E,0.1S,0.1N S0.1,W0.1,N0.1,E0.1] + # That last item in the goodbbox really shouldn't be there, as the API should + # reall reject it, however this is to test to see if the api changes. + end + + ## + # test all routes which lead to this controller + def test_routes + assert_routing( + { :path => "/api/0.6/map", :method => :get }, + { :controller => "api/map", :action => "index" } + ) + end + + # ------------------------------------- + # Test reading a bounding box. + # ------------------------------------- + + def test_map + node = create(:node, :lat => 7, :lon => 7) + tag = create(:node_tag, :node => node) + way1 = create(:way_node, :node => node).way + way2 = create(:way_node, :node => node).way + relation = create(:relation_member, :member => node).relation + + # Need to split the min/max lat/lon out into their own variables here + # so that we can test they are returned later. + minlon = node.lon - 0.1 + minlat = node.lat - 0.1 + maxlon = node.lon + 0.1 + maxlat = node.lat + 0.1 + bbox = "#{minlon},#{minlat},#{maxlon},#{maxlat}" + get :index, :params => { :bbox => bbox } + if $VERBOSE + print @request.to_yaml + print @response.body + end + assert_response :success, "Expected scucess with the map call" + assert_select "osm[version='#{API_VERSION}'][generator='#{GENERATOR}']", :count => 1 do + assert_select "bounds[minlon='#{format('%.7f', minlon)}'][minlat='#{format('%.7f', minlat)}'][maxlon='#{format('%.7f', maxlon)}'][maxlat='#{format('%.7f', maxlat)}']", :count => 1 + assert_select "node[id='#{node.id}'][lat='#{format('%.7f', node.lat)}'][lon='#{format('%.7f', node.lon)}'][version='#{node.version}'][changeset='#{node.changeset_id}'][visible='#{node.visible}'][timestamp='#{node.timestamp.xmlschema}']", :count => 1 do + # This should really be more generic + assert_select "tag[k='#{tag.k}'][v='#{tag.v}']" + end + assert_select "way", :count => 2 + assert_select "way[id='#{way1.id}']", :count => 1 + assert_select "way[id='#{way2.id}']", :count => 1 + assert_select "relation", :count => 1 + assert_select "relation[id='#{relation.id}']", :count => 1 + end + end + + # This differs from the above test in that we are making the bbox exactly + # the same as the node we are looking at + def test_map_inclusive + node = create(:node, :lat => 7, :lon => 7) + tag = create(:node_tag, :node => node) + way1 = create(:way_node, :node => node).way + way2 = create(:way_node, :node => node).way + relation = create(:relation_member, :member => node).relation + + bbox = "#{node.lon},#{node.lat},#{node.lon},#{node.lat}" + get :index, :params => { :bbox => bbox } + assert_response :success, "The map call should have succeeded" + assert_select "osm[version='#{API_VERSION}'][generator='#{GENERATOR}']", :count => 1 do + assert_select "bounds[minlon='#{node.lon}'][minlat='#{node.lat}'][maxlon='#{node.lon}'][maxlat='#{node.lat}']", :count => 1 + assert_select "node[id='#{node.id}'][lat='#{format('%.7f', node.lat)}'][lon='#{format('%.7f', node.lon)}'][version='#{node.version}'][changeset='#{node.changeset_id}'][visible='#{node.visible}'][timestamp='#{node.timestamp.xmlschema}']", :count => 1 do + # This should really be more generic + assert_select "tag[k='#{tag.k}'][v='#{tag.v}']" + end + assert_select "way", :count => 2 + assert_select "way[id='#{way1.id}']", :count => 1 + assert_select "way[id='#{way2.id}']", :count => 1 + assert_select "relation", :count => 1 + assert_select "relation[id='#{relation.id}']", :count => 1 + end + end + + def test_map_complete_way + node = create(:node, :lat => 7, :lon => 7) + # create a couple of nodes well outside of the bbox + node2 = create(:node, :lat => 45, :lon => 45) + node3 = create(:node, :lat => 10, :lon => 10) + way1 = create(:way_node, :node => node).way + create(:way_node, :way => way1, :node => node2, :sequence_id => 2) + way2 = create(:way_node, :node => node).way + create(:way_node, :way => way2, :node => node3, :sequence_id => 2) + relation = create(:relation_member, :member => way1).relation + + bbox = "#{node.lon},#{node.lat},#{node.lon},#{node.lat}" + get :index, :params => { :bbox => bbox } + assert_response :success, "The map call should have succeeded" + assert_select "osm[version='#{API_VERSION}'][generator='#{GENERATOR}']", :count => 1 do + assert_select "bounds[minlon='#{node.lon}'][minlat='#{node.lat}'][maxlon='#{node.lon}'][maxlat='#{node.lat}']", :count => 1 + assert_select "node", :count => 3 + assert_select "node[id='#{node.id}']", :count => 1 + assert_select "node[id='#{node2.id}']", :count => 1 + assert_select "node[id='#{node3.id}']", :count => 1 + assert_select "way", :count => 2 + assert_select "way[id='#{way1.id}']", :count => 1 + assert_select "way[id='#{way2.id}']", :count => 1 + assert_select "relation", :count => 1 + assert_select "relation[id='#{relation.id}']", :count => 1 + end + end + + def test_map_empty + get :index, :params => { :bbox => "179.998,89.998,179.999.1,89.999" } + assert_response :success, "The map call should have succeeded" + assert_select "osm[version='#{API_VERSION}'][generator='#{GENERATOR}']", :count => 1 do + assert_select "bounds[minlon='179.9980000'][minlat='89.9980000'][maxlon='179.9990000'][maxlat='89.9990000']", :count => 1 + assert_select "node", :count => 0 + assert_select "way", :count => 0 + assert_select "relation", :count => 0 + end + end + + def test_map_without_bbox + get :index + assert_response :bad_request + assert_equal "The parameter bbox is required, and must be of the form min_lon,min_lat,max_lon,max_lat", @response.body, "A bbox param was expected" + end + + def test_bbox_too_big + @badbigbbox.each do |bbox| + get :index, :params => { :bbox => bbox } + assert_response :bad_request, "The bbox:#{bbox} was expected to be too big" + assert_equal "The maximum bbox size is #{MAX_REQUEST_AREA}, and your request was too large. Either request a smaller area, or use planet.osm", @response.body, "bbox: #{bbox}" + end + end + + def test_bbox_malformed + @badmalformedbbox.each do |bbox| + get :index, :params => { :bbox => bbox } + assert_response :bad_request, "The bbox:#{bbox} was expected to be malformed" + assert_equal "The parameter bbox is required, and must be of the form min_lon,min_lat,max_lon,max_lat", @response.body, "bbox: #{bbox}" + end + end + + def test_bbox_lon_mixedup + @badlonmixedbbox.each do |bbox| + get :index, :params => { :bbox => bbox } + assert_response :bad_request, "The bbox:#{bbox} was expected to have the longitude mixed up" + assert_equal "The minimum longitude must be less than the maximum longitude, but it wasn't", @response.body, "bbox: #{bbox}" + end + end + + def test_bbox_lat_mixedup + @badlatmixedbbox.each do |bbox| + get :index, :params => { :bbox => bbox } + assert_response :bad_request, "The bbox:#{bbox} was expected to have the latitude mixed up" + assert_equal "The minimum latitude must be less than the maximum latitude, but it wasn't", @response.body, "bbox: #{bbox}" + end + end + + # We can't actually get an out of bounds error, as the bbox is sanitised. + # def test_latlon_outofbounds + # @badlatlonoutboundsbbox.each do |bbox| + # [ "trackpoints", "map" ].each do |tq| + # get tq, :bbox => bbox + # #print @request.to_yaml + # assert_response :bad_request, "The bbox #{bbox} was expected to be out of range" + # assert_equal "The latitudes must be between -90 an 90, and longitudes between -180 and 180", @response.body, "bbox: #{bbox}" + # end + # end + # end + end +end diff --git a/test/controllers/api/permissions_controller_test.rb b/test/controllers/api/permissions_controller_test.rb new file mode 100644 index 0000000000..eb1bfed16b --- /dev/null +++ b/test/controllers/api/permissions_controller_test.rb @@ -0,0 +1,51 @@ +require "test_helper" + +module Api + class PermissionsControllerTest < ActionController::TestCase + ## + # test all routes which lead to this controller + def test_routes + assert_routing( + { :path => "/api/0.6/permissions", :method => :get }, + { :controller => "api/permissions", :action => "show" } + ) + end + + def test_permissions_anonymous + get :show + assert_response :success + assert_select "osm > permissions", :count => 1 do + assert_select "permission", :count => 0 + end + end + + def test_permissions_basic_auth + basic_authorization create(:user).email, "test" + get :show + assert_response :success + assert_select "osm > permissions", :count => 1 do + assert_select "permission", :count => ClientApplication.all_permissions.size + ClientApplication.all_permissions.each do |p| + assert_select "permission[name='#{p}']", :count => 1 + end + end + end + + def test_permissions_oauth + @request.env["oauth.token"] = AccessToken.new do |token| + # Just to test a few + token.allow_read_prefs = true + token.allow_write_api = true + token.allow_read_gpx = false + end + get :show + assert_response :success + assert_select "osm > permissions", :count => 1 do + assert_select "permission", :count => 2 + assert_select "permission[name='allow_read_prefs']", :count => 1 + assert_select "permission[name='allow_write_api']", :count => 1 + assert_select "permission[name='allow_read_gpx']", :count => 0 + end + end + end +end diff --git a/test/controllers/api/tracepoints_controller_test.rb b/test/controllers/api/tracepoints_controller_test.rb new file mode 100644 index 0000000000..b9a1c126f3 --- /dev/null +++ b/test/controllers/api/tracepoints_controller_test.rb @@ -0,0 +1,146 @@ +require "test_helper" + +module Api + class TracepointsControllerTest < ActionController::TestCase + def setup + super + @badbigbbox = %w[-0.1,-0.1,1.1,1.1 10,10,11,11] + @badmalformedbbox = %w[-0.1 hello + 10N2W10.1N2.1W] + @badlatmixedbbox = %w[0,0.1,0.1,0 -0.1,80,0.1,70 0.24,54.34,0.25,54.33] + @badlonmixedbbox = %w[80,-0.1,70,0.1 54.34,0.24,54.33,0.25] + # @badlatlonoutboundsbbox = %w{ 191,-0.1,193,0.1 -190.1,89.9,-190,90 } + @goodbbox = %w[-0.1,-0.1,0.1,0.1 51.1,-0.1,51.2,0 + -0.1,%20-0.1,%200.1,%200.1 -0.1edcd,-0.1d,0.1,0.1 -0.1E,-0.1E,0.1S,0.1N S0.1,W0.1,N0.1,E0.1] + # That last item in the goodbbox really shouldn't be there, as the API should + # reall reject it, however this is to test to see if the api changes. + end + + ## + # test all routes which lead to this controller + def test_routes + assert_routing( + { :path => "/api/0.6/trackpoints", :method => :get }, + { :controller => "api/tracepoints", :action => "index" } + ) + end + + def test_tracepoints + point = create(:trace, :visibility => "public", :latitude => 1, :longitude => 1) do |trace| + create(:tracepoint, :trace => trace, :latitude => 1 * GeoRecord::SCALE, :longitude => 1 * GeoRecord::SCALE) + end + minlon = point.longitude - 0.001 + minlat = point.latitude - 0.001 + maxlon = point.longitude + 0.001 + maxlat = point.latitude + 0.001 + bbox = "#{minlon},#{minlat},#{maxlon},#{maxlat}" + get :index, :params => { :bbox => bbox } + assert_response :success + assert_select "gpx[version='1.0'][creator='OpenStreetMap.org']", :count => 1 do + assert_select "trk" do + assert_select "trkseg" + end + end + end + + def test_tracepoints_trackable + point = create(:trace, :visibility => "trackable", :latitude => 51.51, :longitude => -0.14) do |trace| + create(:tracepoint, :trace => trace, :trackid => 1, :latitude => (51.510 * GeoRecord::SCALE).to_i, :longitude => (-0.140 * GeoRecord::SCALE).to_i) + create(:tracepoint, :trace => trace, :trackid => 2, :latitude => (51.511 * GeoRecord::SCALE).to_i, :longitude => (-0.141 * GeoRecord::SCALE).to_i) + end + minlon = point.longitude - 0.002 + minlat = point.latitude - 0.002 + maxlon = point.longitude + 0.002 + maxlat = point.latitude + 0.002 + bbox = "#{minlon},#{minlat},#{maxlon},#{maxlat}" + get :index, :params => { :bbox => bbox } + assert_response :success + assert_select "gpx[version='1.0'][creator='OpenStreetMap.org']", :count => 1 do + assert_select "trk", :count => 1 do + assert_select "trk > trkseg", :count => 2 do |trksegs| + trksegs.each do |trkseg| + assert_select trkseg, "trkpt", :count => 1 do |trkpt| + assert_select trkpt[0], "time", :count => 1 + end + end + end + end + end + end + + def test_tracepoints_identifiable + point = create(:trace, :visibility => "identifiable", :latitude => 51.512, :longitude => 0.142) do |trace| + create(:tracepoint, :trace => trace, :latitude => (51.512 * GeoRecord::SCALE).to_i, :longitude => (0.142 * GeoRecord::SCALE).to_i) + end + minlon = point.longitude - 0.002 + minlat = point.latitude - 0.002 + maxlon = point.longitude + 0.002 + maxlat = point.latitude + 0.002 + bbox = "#{minlon},#{minlat},#{maxlon},#{maxlat}" + get :index, :params => { :bbox => bbox } + assert_response :success + assert_select "gpx[version='1.0'][creator='OpenStreetMap.org']", :count => 1 do + assert_select "trk", :count => 1 do + assert_select "trk>name", :count => 1 + assert_select "trk>desc", :count => 1 + assert_select "trk>url", :count => 1 + assert_select "trkseg", :count => 1 do + assert_select "trkpt", :count => 1 do + assert_select "time", :count => 1 + end + end + end + end + end + + def test_index_without_bbox + get :index + assert_response :bad_request + assert_equal "The parameter bbox is required, and must be of the form min_lon,min_lat,max_lon,max_lat", @response.body, "A bbox param was expected" + end + + def test_traces_page_less_than_0 + -10.upto(-1) do |i| + get :index, :params => { :page => i, :bbox => "-0.1,-0.1,0.1,0.1" } + assert_response :bad_request + assert_equal "Page number must be greater than or equal to 0", @response.body, "The page number was #{i}" + end + 0.upto(10) do |i| + get :index, :params => { :page => i, :bbox => "-0.1,-0.1,0.1,0.1" } + assert_response :success, "The page number was #{i} and should have been accepted" + end + end + + def test_bbox_too_big + @badbigbbox.each do |bbox| + get :index, :params => { :bbox => bbox } + assert_response :bad_request, "The bbox:#{bbox} was expected to be too big" + assert_equal "The maximum bbox size is #{MAX_REQUEST_AREA}, and your request was too large. Either request a smaller area, or use planet.osm", @response.body, "bbox: #{bbox}" + end + end + + def test_bbox_malformed + @badmalformedbbox.each do |bbox| + get :index, :params => { :bbox => bbox } + assert_response :bad_request, "The bbox:#{bbox} was expected to be malformed" + assert_equal "The parameter bbox is required, and must be of the form min_lon,min_lat,max_lon,max_lat", @response.body, "bbox: #{bbox}" + end + end + + def test_bbox_lon_mixedup + @badlonmixedbbox.each do |bbox| + get :index, :params => { :bbox => bbox } + assert_response :bad_request, "The bbox:#{bbox} was expected to have the longitude mixed up" + assert_equal "The minimum longitude must be less than the maximum longitude, but it wasn't", @response.body, "bbox: #{bbox}" + end + end + + def test_bbox_lat_mixedup + @badlatmixedbbox.each do |bbox| + get :index, :params => { :bbox => bbox } + assert_response :bad_request, "The bbox:#{bbox} was expected to have the latitude mixed up" + assert_equal "The minimum latitude must be less than the maximum latitude, but it wasn't", @response.body, "bbox: #{bbox}" + end + end + end +end diff --git a/test/controllers/api_controller_test.rb b/test/controllers/api_controller_test.rb deleted file mode 100644 index cdc20a1aad..0000000000 --- a/test/controllers/api_controller_test.rb +++ /dev/null @@ -1,435 +0,0 @@ -require "test_helper" - -class ApiControllerTest < ActionController::TestCase - def setup - super - @badbigbbox = %w[-0.1,-0.1,1.1,1.1 10,10,11,11] - @badmalformedbbox = %w[-0.1 hello - 10N2W10.1N2.1W] - @badlatmixedbbox = %w[0,0.1,0.1,0 -0.1,80,0.1,70 0.24,54.34,0.25,54.33] - @badlonmixedbbox = %w[80,-0.1,70,0.1 54.34,0.24,54.33,0.25] - # @badlatlonoutboundsbbox = %w{ 191,-0.1,193,0.1 -190.1,89.9,-190,90 } - @goodbbox = %w[-0.1,-0.1,0.1,0.1 51.1,-0.1,51.2,0 - -0.1,%20-0.1,%200.1,%200.1 -0.1edcd,-0.1d,0.1,0.1 -0.1E,-0.1E,0.1S,0.1N S0.1,W0.1,N0.1,E0.1] - # That last item in the goodbbox really shouldn't be there, as the API should - # reall reject it, however this is to test to see if the api changes. - end - - ## - # test all routes which lead to this controller - def test_routes - assert_routing( - { :path => "/api/capabilities", :method => :get }, - { :controller => "api", :action => "capabilities" } - ) - assert_recognizes( - { :controller => "api", :action => "capabilities" }, - { :path => "/api/0.6/capabilities", :method => :get } - ) - assert_routing( - { :path => "/api/0.6/permissions", :method => :get }, - { :controller => "api", :action => "permissions" } - ) - assert_routing( - { :path => "/api/0.6/map", :method => :get }, - { :controller => "api", :action => "map" } - ) - assert_routing( - { :path => "/api/0.6/trackpoints", :method => :get }, - { :controller => "api", :action => "trackpoints" } - ) - assert_routing( - { :path => "/api/0.6/changes", :method => :get }, - { :controller => "api", :action => "changes" } - ) - end - - # ------------------------------------- - # Test reading a bounding box. - # ------------------------------------- - - def test_map - node = create(:node, :lat => 7, :lon => 7) - tag = create(:node_tag, :node => node) - way1 = create(:way_node, :node => node).way - way2 = create(:way_node, :node => node).way - relation = create(:relation_member, :member => node).relation - - # Need to split the min/max lat/lon out into their own variables here - # so that we can test they are returned later. - minlon = node.lon - 0.1 - minlat = node.lat - 0.1 - maxlon = node.lon + 0.1 - maxlat = node.lat + 0.1 - bbox = "#{minlon},#{minlat},#{maxlon},#{maxlat}" - get :map, :params => { :bbox => bbox } - if $VERBOSE - print @request.to_yaml - print @response.body - end - assert_response :success, "Expected scucess with the map call" - assert_select "osm[version='#{API_VERSION}'][generator='#{GENERATOR}']", :count => 1 do - assert_select "bounds[minlon='#{format('%.7f', minlon)}'][minlat='#{format('%.7f', minlat)}'][maxlon='#{format('%.7f', maxlon)}'][maxlat='#{format('%.7f', maxlat)}']", :count => 1 - assert_select "node[id='#{node.id}'][lat='#{format('%.7f', node.lat)}'][lon='#{format('%.7f', node.lon)}'][version='#{node.version}'][changeset='#{node.changeset_id}'][visible='#{node.visible}'][timestamp='#{node.timestamp.xmlschema}']", :count => 1 do - # This should really be more generic - assert_select "tag[k='#{tag.k}'][v='#{tag.v}']" - end - assert_select "way", :count => 2 - assert_select "way[id='#{way1.id}']", :count => 1 - assert_select "way[id='#{way2.id}']", :count => 1 - assert_select "relation", :count => 1 - assert_select "relation[id='#{relation.id}']", :count => 1 - end - end - - # This differs from the above test in that we are making the bbox exactly - # the same as the node we are looking at - def test_map_inclusive - node = create(:node, :lat => 7, :lon => 7) - tag = create(:node_tag, :node => node) - way1 = create(:way_node, :node => node).way - way2 = create(:way_node, :node => node).way - relation = create(:relation_member, :member => node).relation - - bbox = "#{node.lon},#{node.lat},#{node.lon},#{node.lat}" - get :map, :params => { :bbox => bbox } - assert_response :success, "The map call should have succeeded" - assert_select "osm[version='#{API_VERSION}'][generator='#{GENERATOR}']", :count => 1 do - assert_select "bounds[minlon='#{node.lon}'][minlat='#{node.lat}'][maxlon='#{node.lon}'][maxlat='#{node.lat}']", :count => 1 - assert_select "node[id='#{node.id}'][lat='#{format('%.7f', node.lat)}'][lon='#{format('%.7f', node.lon)}'][version='#{node.version}'][changeset='#{node.changeset_id}'][visible='#{node.visible}'][timestamp='#{node.timestamp.xmlschema}']", :count => 1 do - # This should really be more generic - assert_select "tag[k='#{tag.k}'][v='#{tag.v}']" - end - assert_select "way", :count => 2 - assert_select "way[id='#{way1.id}']", :count => 1 - assert_select "way[id='#{way2.id}']", :count => 1 - assert_select "relation", :count => 1 - assert_select "relation[id='#{relation.id}']", :count => 1 - end - end - - def test_map_complete_way - node = create(:node, :lat => 7, :lon => 7) - # create a couple of nodes well outside of the bbox - node2 = create(:node, :lat => 45, :lon => 45) - node3 = create(:node, :lat => 10, :lon => 10) - way1 = create(:way_node, :node => node).way - create(:way_node, :way => way1, :node => node2, :sequence_id => 2) - way2 = create(:way_node, :node => node).way - create(:way_node, :way => way2, :node => node3, :sequence_id => 2) - relation = create(:relation_member, :member => way1).relation - - bbox = "#{node.lon},#{node.lat},#{node.lon},#{node.lat}" - get :map, :params => { :bbox => bbox } - assert_response :success, "The map call should have succeeded" - assert_select "osm[version='#{API_VERSION}'][generator='#{GENERATOR}']", :count => 1 do - assert_select "bounds[minlon='#{node.lon}'][minlat='#{node.lat}'][maxlon='#{node.lon}'][maxlat='#{node.lat}']", :count => 1 - assert_select "node", :count => 3 - assert_select "node[id='#{node.id}']", :count => 1 - assert_select "node[id='#{node2.id}']", :count => 1 - assert_select "node[id='#{node3.id}']", :count => 1 - assert_select "way", :count => 2 - assert_select "way[id='#{way1.id}']", :count => 1 - assert_select "way[id='#{way2.id}']", :count => 1 - assert_select "relation", :count => 1 - assert_select "relation[id='#{relation.id}']", :count => 1 - end - end - - def test_map_empty - get :map, :params => { :bbox => "179.998,89.998,179.999.1,89.999" } - assert_response :success, "The map call should have succeeded" - assert_select "osm[version='#{API_VERSION}'][generator='#{GENERATOR}']", :count => 1 do - assert_select "bounds[minlon='179.9980000'][minlat='89.9980000'][maxlon='179.9990000'][maxlat='89.9990000']", :count => 1 - assert_select "node", :count => 0 - assert_select "way", :count => 0 - assert_select "relation", :count => 0 - end - end - - def test_tracepoints - point = create(:trace, :visibility => "public", :latitude => 1, :longitude => 1) do |trace| - create(:tracepoint, :trace => trace, :latitude => 1 * GeoRecord::SCALE, :longitude => 1 * GeoRecord::SCALE) - end - minlon = point.longitude - 0.001 - minlat = point.latitude - 0.001 - maxlon = point.longitude + 0.001 - maxlat = point.latitude + 0.001 - bbox = "#{minlon},#{minlat},#{maxlon},#{maxlat}" - get :trackpoints, :params => { :bbox => bbox } - assert_response :success - assert_select "gpx[version='1.0'][creator='OpenStreetMap.org']", :count => 1 do - assert_select "trk" do - assert_select "trkseg" - end - end - end - - def test_tracepoints_trackable - point = create(:trace, :visibility => "trackable", :latitude => 51.51, :longitude => -0.14) do |trace| - create(:tracepoint, :trace => trace, :trackid => 1, :latitude => (51.510 * GeoRecord::SCALE).to_i, :longitude => (-0.140 * GeoRecord::SCALE).to_i) - create(:tracepoint, :trace => trace, :trackid => 2, :latitude => (51.511 * GeoRecord::SCALE).to_i, :longitude => (-0.141 * GeoRecord::SCALE).to_i) - end - minlon = point.longitude - 0.002 - minlat = point.latitude - 0.002 - maxlon = point.longitude + 0.002 - maxlat = point.latitude + 0.002 - bbox = "#{minlon},#{minlat},#{maxlon},#{maxlat}" - get :trackpoints, :params => { :bbox => bbox } - assert_response :success - assert_select "gpx[version='1.0'][creator='OpenStreetMap.org']", :count => 1 do - assert_select "trk", :count => 1 do - assert_select "trk > trkseg", :count => 2 do |trksegs| - trksegs.each do |trkseg| - assert_select trkseg, "trkpt", :count => 1 do |trkpt| - assert_select trkpt[0], "time", :count => 1 - end - end - end - end - end - end - - def test_tracepoints_identifiable - point = create(:trace, :visibility => "identifiable", :latitude => 51.512, :longitude => 0.142) do |trace| - create(:tracepoint, :trace => trace, :latitude => (51.512 * GeoRecord::SCALE).to_i, :longitude => (0.142 * GeoRecord::SCALE).to_i) - end - minlon = point.longitude - 0.002 - minlat = point.latitude - 0.002 - maxlon = point.longitude + 0.002 - maxlat = point.latitude + 0.002 - bbox = "#{minlon},#{minlat},#{maxlon},#{maxlat}" - get :trackpoints, :params => { :bbox => bbox } - assert_response :success - assert_select "gpx[version='1.0'][creator='OpenStreetMap.org']", :count => 1 do - assert_select "trk", :count => 1 do - assert_select "trk>name", :count => 1 - assert_select "trk>desc", :count => 1 - assert_select "trk>url", :count => 1 - assert_select "trkseg", :count => 1 do - assert_select "trkpt", :count => 1 do - assert_select "time", :count => 1 - end - end - end - end - end - - def test_map_without_bbox - %w[trackpoints map].each do |tq| - get tq - assert_response :bad_request - assert_equal "The parameter bbox is required, and must be of the form min_lon,min_lat,max_lon,max_lat", @response.body, "A bbox param was expected" - end - end - - def test_traces_page_less_than_0 - -10.upto(-1) do |i| - get :trackpoints, :params => { :page => i, :bbox => "-0.1,-0.1,0.1,0.1" } - assert_response :bad_request - assert_equal "Page number must be greater than or equal to 0", @response.body, "The page number was #{i}" - end - 0.upto(10) do |i| - get :trackpoints, :params => { :page => i, :bbox => "-0.1,-0.1,0.1,0.1" } - assert_response :success, "The page number was #{i} and should have been accepted" - end - end - - def test_bbox_too_big - @badbigbbox.each do |bbox| - %w[trackpoints map].each do |tq| - get tq, :params => { :bbox => bbox } - assert_response :bad_request, "The bbox:#{bbox} was expected to be too big" - assert_equal "The maximum bbox size is #{MAX_REQUEST_AREA}, and your request was too large. Either request a smaller area, or use planet.osm", @response.body, "bbox: #{bbox}" - end - end - end - - def test_bbox_malformed - @badmalformedbbox.each do |bbox| - %w[trackpoints map].each do |tq| - get tq, :params => { :bbox => bbox } - assert_response :bad_request, "The bbox:#{bbox} was expected to be malformed" - assert_equal "The parameter bbox is required, and must be of the form min_lon,min_lat,max_lon,max_lat", @response.body, "bbox: #{bbox}" - end - end - end - - def test_bbox_lon_mixedup - @badlonmixedbbox.each do |bbox| - %w[trackpoints map].each do |tq| - get tq, :params => { :bbox => bbox } - assert_response :bad_request, "The bbox:#{bbox} was expected to have the longitude mixed up" - assert_equal "The minimum longitude must be less than the maximum longitude, but it wasn't", @response.body, "bbox: #{bbox}" - end - end - end - - def test_bbox_lat_mixedup - @badlatmixedbbox.each do |bbox| - %w[trackpoints map].each do |tq| - get tq, :params => { :bbox => bbox } - assert_response :bad_request, "The bbox:#{bbox} was expected to have the latitude mixed up" - assert_equal "The minimum latitude must be less than the maximum latitude, but it wasn't", @response.body, "bbox: #{bbox}" - end - end - end - - # We can't actually get an out of bounds error, as the bbox is sanitised. - # def test_latlon_outofbounds - # @badlatlonoutboundsbbox.each do |bbox| - # [ "trackpoints", "map" ].each do |tq| - # get tq, :bbox => bbox - # #print @request.to_yaml - # assert_response :bad_request, "The bbox #{bbox} was expected to be out of range" - # assert_equal "The latitudes must be between -90 an 90, and longitudes between -180 and 180", @response.body, "bbox: #{bbox}" - # end - # end - # end - - # MySQL and Postgres require that the C based functions are installed for - # this test to work. More information is available from: - # http://wiki.openstreetmap.org/wiki/Rails#Installing_the_quadtile_functions - # or by looking at the readme in db/README - def test_changes_simple - # create a selection of nodes - (1..5).each do |n| - create(:node, :timestamp => Time.utc(2007, 1, 1, 0, 0, 0), :lat => n, :lon => n) - end - # deleted nodes should also be counted - create(:node, :deleted, :timestamp => Time.utc(2007, 1, 1, 0, 0, 0), :lat => 6, :lon => 6) - # nodes in the same tile won't change the total - create(:node, :timestamp => Time.utc(2007, 1, 1, 0, 0, 0), :lat => 6, :lon => 6) - # nodes with a different timestamp should be ignored - create(:node, :timestamp => Time.utc(2008, 1, 1, 0, 0, 0), :lat => 7, :lon => 7) - - travel_to Time.utc(2010, 4, 3, 10, 55, 0) do - get :changes - assert_response :success - now = Time.now.getutc - hourago = now - 1.hour - assert_select "osm[version='#{API_VERSION}'][generator='#{GENERATOR}']", :count => 1 do - assert_select "changes[starttime='#{hourago.xmlschema}'][endtime='#{now.xmlschema}']", :count => 1 do - assert_select "tile", :count => 0 - end - end - end - - travel_to Time.utc(2007, 1, 1, 0, 30, 0) do - get :changes - assert_response :success - # print @response.body - # As we have loaded the fixtures, we can assume that there are some - # changes at the time we have frozen at - now = Time.now.getutc - hourago = now - 1.hour - assert_select "osm[version='#{API_VERSION}'][generator='#{GENERATOR}']", :count => 1 do - assert_select "changes[starttime='#{hourago.xmlschema}'][endtime='#{now.xmlschema}']", :count => 1 do - assert_select "tile", :count => 6 - end - end - end - end - - def test_changes_zoom_invalid - zoom_to_test = %w[p -1 0 17 one two] - zoom_to_test.each do |zoom| - get :changes, :params => { :zoom => zoom } - assert_response :bad_request - assert_equal @response.body, "Requested zoom is invalid, or the supplied start is after the end time, or the start duration is more than 24 hours" - end - end - - def test_changes_zoom_valid - 1.upto(16) do |zoom| - get :changes, :params => { :zoom => zoom } - assert_response :success - # NOTE: there was a test here for the timing, but it was too sensitive to be a good test - # and it was annoying. - assert_select "osm[version='#{API_VERSION}'][generator='#{GENERATOR}']", :count => 1 do - assert_select "changes", :count => 1 - end - end - end - - def test_changes_hours_invalid - invalid = %w[-21 335 -1 0 25 26 100 one two three ping pong :] - invalid.each do |hour| - get :changes, :params => { :hours => hour } - assert_response :bad_request, "Problem with the hour: #{hour}" - assert_equal @response.body, "Requested zoom is invalid, or the supplied start is after the end time, or the start duration is more than 24 hours", "Problem with the hour: #{hour}." - end - end - - def test_changes_hours_valid - 1.upto(24) do |hour| - get :changes, :params => { :hours => hour } - assert_response :success - end - end - - def test_changes_start_end_invalid - get :changes, :params => { :start => "2010-04-03 10:55:00", :end => "2010-04-03 09:55:00" } - assert_response :bad_request - assert_equal @response.body, "Requested zoom is invalid, or the supplied start is after the end time, or the start duration is more than 24 hours" - end - - def test_changes_start_end_valid - get :changes, :params => { :start => "2010-04-03 09:55:00", :end => "2010-04-03 10:55:00" } - assert_response :success - end - - def test_capabilities - get :capabilities - assert_response :success - assert_select "osm[version='#{API_VERSION}'][generator='#{GENERATOR}']", :count => 1 do - assert_select "api", :count => 1 do - assert_select "version[minimum='#{API_VERSION}'][maximum='#{API_VERSION}']", :count => 1 - assert_select "area[maximum='#{MAX_REQUEST_AREA}']", :count => 1 - assert_select "note_area[maximum='#{MAX_NOTE_REQUEST_AREA}']", :count => 1 - assert_select "tracepoints[per_page='#{TRACEPOINTS_PER_PAGE}']", :count => 1 - assert_select "changesets[maximum_elements='#{Changeset::MAX_ELEMENTS}']", :count => 1 - assert_select "status[database='online']", :count => 1 - assert_select "status[api='online']", :count => 1 - assert_select "status[gpx='online']", :count => 1 - end - end - end - - def test_permissions_anonymous - get :permissions - assert_response :success - assert_select "osm > permissions", :count => 1 do - assert_select "permission", :count => 0 - end - end - - def test_permissions_basic_auth - basic_authorization create(:user).email, "test" - get :permissions - assert_response :success - assert_select "osm > permissions", :count => 1 do - assert_select "permission", :count => ClientApplication.all_permissions.size - ClientApplication.all_permissions.each do |p| - assert_select "permission[name='#{p}']", :count => 1 - end - end - end - - def test_permissions_oauth - @request.env["oauth.token"] = AccessToken.new do |token| - # Just to test a few - token.allow_read_prefs = true - token.allow_write_api = true - token.allow_read_gpx = false - end - get :permissions - assert_response :success - assert_select "osm > permissions", :count => 1 do - assert_select "permission", :count => 2 - assert_select "permission[name='allow_read_prefs']", :count => 1 - assert_select "permission[name='allow_write_api']", :count => 1 - assert_select "permission[name='allow_read_gpx']", :count => 0 - end - end -end diff --git a/test/controllers/export_controller_test.rb b/test/controllers/export_controller_test.rb index 94cf6cf52e..daaf193c89 100644 --- a/test/controllers/export_controller_test.rb +++ b/test/controllers/export_controller_test.rb @@ -19,7 +19,7 @@ def test_routes def test_finish_osm get :finish, :params => { :minlon => 0, :minlat => 50, :maxlon => 1, :maxlat => 51, :format => "osm" } assert_response :redirect - assert_redirected_to "controller" => "api", "action" => "map", "bbox" => "0.0,50.0,1.0,51.0" + assert_redirected_to "controller" => "api/map", "action" => "index", "bbox" => "0.0,50.0,1.0,51.0" end ###