require 'em-synchrony' require 'em-synchrony/em-http' require 'goliath/server' require 'rack' module Goliath # Methods to help with testing Goliath APIs # # @example # describe Echo do # include Goliath::TestHelper # # let(:err) { Proc.new { fail "API request failed" } } # it 'returns the echo param' do # with_api(Echo) do # get_request({:query => {:echo => 'test'}}, err) do |c| # b = Yajl::Parser.parse(c.response) # b['response'].should == 'test' # end # end # end # end # module TestHelper def self.included(mod) Goliath.env = 'test' end # Builds the rack middleware chain for the given API # # @param klass [Class] The API class to build the middlewares for # @return [Object] The Rack middleware chain def build_app(klass) ::Rack::Builder.new do klass.middlewares.each do |mw| use(*(mw[0..1].compact), &mw[2]) end run klass.new end end # Launches an instance of a given API server. The server # will launch on the default settings of localhost port 9000. # # @param api [Class] The API class to launch # @param port [Integer] The port to run the server on # @param options [Hash] The options hash to provide to the server # @return [Goliath::Server] The executed server def server(api, port = 9000, options = {}) op = OptionParser.new s = Goliath::Server.new s.logger = mock('log').as_null_object s.api = api.new s.app = build_app(api) s.api.options_parser(op, options) s.options = options s.port = port s.start s end # Stops the launched API # # @return [Nil] def stop EM.stop end # Wrapper for launching API and executing given code block. This # will start the EventMachine reactor running. # # @param api [Class] The API class to launch # @param options [Hash] The options to pass to the server # @param blk [Proc] The code to execute after the server is launched. # @note This will not return until stop is called. def with_api(api, options = {}, &blk) EM.synchrony do @api_server = server(api, 9000, options) blk.call end end # Helper method to setup common callbacks for various request methods. # The given err and callback handlers will be attached and a callback # to stop the reactor will be added. # # @param req [EM::HttpRequest] The HTTP request to augment # @param errback [Proc] An error handler to attach # @param blk [Proc] The callback handler to attach # @return [Nil] # @api private def hookup_request_callbacks(req, errback, &blk) req.callback &blk req.callback { stop } req.errback &errback if errback req.errback { stop } end # Make a GET request the currently launched API. # # @param request_data [Hash] Any data to pass to the GET request. # @param errback [Proc] An error handler to attach # @param blk [Proc] The callback block to execute def get_request(request_data = {}, errback = nil, &blk) path = request_data.delete(:path) || '' req = EM::HttpRequest.new("http://localhost:9000#{path}").get(request_data) hookup_request_callbacks(req, errback, &blk) end # Make a POST request the currently launched API. # # @param request_data [Hash] Any data to pass to the POST request. # @param errback [Proc] An error handler to attach # @param blk [Proc] The callback block to execute def post_request(request_data = {}, errback = nil, &blk) path = request_data.delete(:path) || '' req = EM::HttpRequest.new("http://localhost:9000#{path}").post(request_data) hookup_request_callbacks(req, errback, &blk) end end end