maestrano/mno-enterprise

View on GitHub
core/lib/mno_enterprise/testing_support/mnoe_faraday_test_adapter.rb

Summary

Maintainability
A
2 hrs
Test Coverage
# Adapter and modified from: https://github.com/lostisland/faraday/blob/master/lib/faraday/adapter/test.rb
#
#
#
# test = Faraday::Connection.new do
#   use MnoeFaradayTestAdapter do |stub|
#     stub.get '/nigiri/sake.json' do
#       [200, {}, 'hi world']
#     end
#   end
# end
#
# resp = test.get '/nigiri/sake.json'
# resp.body # => 'hi world'
#
class MnoeFaradayTestAdapter < Faraday::Adapter
  attr_accessor :stubs

  class Stubs
    class NotFound < StandardError
    end

    def initialize
      # {:get => [Stub, Stub]}
      @stack, @consumed = {}, {}
      yield(self) if block_given?
    end

    def empty?
      @stack.empty?
    end

    def match(request_method, path, headers, body)
      return false if !@stack.key?(request_method)
      stack = @stack[request_method]
      consumed = (@consumed[request_method] ||= [])

      if stub = matches?(stack, path, headers, body)
        consumed << stack.delete(stub)
        stub
      else
        matches?(consumed, path, headers, body)
      end
    end

    def get(path, headers = {}, &block)
      new_stub(:get, path, headers, &block)
    end

    def head(path, headers = {}, &block)
      new_stub(:head, path, headers, &block)
    end

    def post(path, body=nil, headers = {}, &block)
      new_stub(:post, path, headers, body, &block)
    end

    def put(path, body=nil, headers = {}, &block)
      new_stub(:put, path, headers, body, &block)
    end

    def patch(path, body=nil, headers = {}, &block)
      new_stub(:patch, path, headers, body, &block)
    end

    def delete(path, headers = {}, &block)
      new_stub(:delete, path, headers, &block)
    end

    def options(path, headers = {}, &block)
      new_stub(:options, path, headers, &block)
    end

    # Raises an error if any of the stubbed calls have not been made.
    def verify_stubbed_calls
      failed_stubs = []
      @stack.each do |method, stubs|
        unless stubs.size == 0
          failed_stubs.concat(stubs.map {|stub|
            "Expected #{method} #{stub}."
          })
        end
      end
      raise failed_stubs.join(" ") unless failed_stubs.size == 0
    end

    protected

    def new_stub(request_method, path, headers = {}, body=nil, &block)
      normalized_path = Faraday::Utils.normalize_path(path)
      (@stack[request_method] ||= []) << Stub.new(normalized_path, headers, body, block)
    end

    def matches?(stack, path, headers, body)
      stack.detect { |stub| stub.matches?(path, headers, body) }
    end
  end

  class Stub < Struct.new(:path, :params, :headers, :body, :block)
    def initialize(full, headers, body, block)
      path, query = full.split('?')
      params = query ?
        Faraday::Utils.parse_nested_query(query) :
        {}
      super(path, params, headers, body, block)
    end

    def matches?(request_uri, request_headers, request_body)
      request_path, request_query = request_uri.split('?')
      request_params = request_query ?
        Faraday::Utils.parse_nested_query(request_query) :
        {}
      request_path == path &&
        params_match?(request_params) &&
        (body.to_s.size.zero? || request_body == body) &&
        headers_match?(request_headers)
    end

    def params_match?(request_params)
      params_deep_equal?(params,request_params)
      # params.keys.all? do |key|
      #   request_params[key] == params[key]
      # end
    end
    
    def params_deep_equal?(src,dst)
      src.keys.all? do |key|
        if src[key] && dst[key] && src[key].is_a?(Hash) && dst[key].is_a?(Hash)
          params_deep_equal?(src[key],dst[key])
        else
          (src[key] == '**' && dst[key]) || src[key] == dst[key]
        end
      end
    end

    def headers_match?(request_headers)
      headers.keys.all? do |key|
        request_headers[key] == headers[key]
      end
    end

    def to_s
      "#{path} #{body}"
    end
  end

  def initialize(app, stubs=nil, &block)
    super(app)
    @stubs = stubs || Stubs.new
    configure(&block) if block
  end

  def configure
    yield(stubs)
  end

  def call(env)
    super
    normalized_path = Faraday::Utils.normalize_path(env[:url])
    params_encoder = env.request.params_encoder || Faraday::Utils.default_params_encoder

    if stub = stubs.match(env[:method], normalized_path, env.request_headers, env[:body])
      env[:params] = (query = env[:url].query) ?
        params_encoder.decode(query)  :
        {}
      status, headers, body = stub.block.call(env)
      save_response(env, status, body, headers)
    else
      raise Stubs::NotFound, "no stubbed request for #{env[:method]} #{normalized_path} #{env[:body]}"
    end
    @app.call(env)
  end
end