core/lib/mno_enterprise/testing_support/mno_enterprise_api_test_helper.rb
module MnoEnterpriseApiTestHelper
# Take a resource and transform it into a Hash describing
# the resource as if it had been returned by the MnoEnterprise
# API server
def from_api(res)
{ data: serialize_type(res), metadata: {pagination: {count: entity_count(res)}} }
end
def serialize_type(res)
case
when res.kind_of?(Array)
return res.map { |e| serialize_type(e) }
when res.kind_of?(MnoEnterprise::BaseResource)
hash = res.attributes.dup
hash.each do |k,v|
hash[k] = serialize_type(v)
end
return hash
when res.kind_of?(Hash)
hash = res.dup
hash.each do |k,v|
hash[k] = serialize_type(v)
end
return hash
when res.kind_of?(Money)
return { cents: res.cents, currency: res.currency.to_s }
when res.respond_to?(:iso8601)
return res.iso8601
else
return res
end
end
def entity_count(res)
case
when res.kind_of?(Array)
return res.count
when res.kind_of?(Hash)
return res.count
else
return 1
end
end
# Reset all API stubs.
# Called before each test (see spec_helper)
def api_stub_reset
@_api_stub = nil
@_stub_list = {}
api_stub_configure(Her::API.new)
end
# Example usage:
#
# Without opts, it yields a faraday stub object which you can configure
# manually:
#
# You can also pass the response stub via opts
# api_stub_for(User,
# path: '/users/popular',
# response: [{ id: 1, name: "Tobias Fünke" }, { id: 2, name: "Lindsay Fünke" }]
# )
#
# You can also specify the response code:
# api_stub_for(User,
# path: '/users/popular',
# code: 200,
# response: [{ id: 1, name: "Tobias Fünke" }, { id: 2, name: "Lindsay Fünke" }]
# )
def api_stub_for(klass, opts = {})
real_opts = klass
if klass.is_a?(Class)
warn("DEPRECATION WARNING: api_stub_for(MyClass,{ some: 'opts'}) is deprecated. Please use api_stub_for({ some: 'opts' }) from now on")
real_opts = opts
end
set_api_stub
api_stub_add(real_opts)
api_stub_configure(@_api_stub)
end
# Remove an API stub added with `api_stub_for`
# This needs to be called with the same options
def remove_api_stub(opts = {})
set_api_stub
api_stub_remove(opts)
api_stub_configure(@_api_stub)
end
# Remove all api stubs
def clear_api_stubs
set_api_stub
@_stub_list = {}
api_stub_configure(@_api_stub)
end
private
# Set a stub api on the provider class
def set_api_stub
return @_api_stub if @_api_stub
@_api_stub = Her::API.new
allow(MnoEnterprise::BaseResource).to receive(:her_api).and_return(@_api_stub = Her::API.new)
@_api_stub
end
# Add a stub to the api
# E.g.:
# {
# path: '/users/popular',
# code: 200,
# response: [{ id: 1, name: "Tobias Fünke" }, { id: 2, name: "Lindsay Fünke" }]
# }
def api_stub_add(orig_opts)
@_stub_list ||= {}
opts = orig_opts.dup
expand_options(opts)
key = opts.to_param
@_stub_list[key] = opts
end
# Remove an API
# This need to be called with the exact same options as `api_stub_add` was called with
def api_stub_remove(orig_opts)
@_stub_list ||= {}
opts = orig_opts.dup
expand_options(opts)
key = opts.to_param
@_stub_list.delete(key)
end
# Expand options so that: { put: '/path' } becomes { path: '/path', method: :put }
def expand_options(opts)
unless opts[:method] && opts[:path]
[:get, :put, :post, :delete].each do |verb|
if path = opts.delete(verb)
opts[:path] = path
opts[:method] = verb
end
end
end
end
# Configure the api and apply a list of stubs
def api_stub_configure(api)
# This block should match the her.rb initializer
api.setup MnoEnterprise.send(:api_options).merge(url: "http://localhost:65000") do |c|
# Request
c.use Faraday::Request::BasicAuthentication, MnoEnterprise.tenant_id, MnoEnterprise.tenant_key
c.use Faraday::Request::UrlEncoded
# Response
c.use Her::Middleware::MnoeApiV1ParseJson
c.use Her::Middleware::MnoeRaiseError
# Add stubs on the test adapter
c.use MnoeFaradayTestAdapter do |receiver|
@_stub_list.each do |key,stub|
params = stub[:params] && stub[:params].any? ? "?#{stub[:params].to_param}" : ""
path = "#{stub[:path]}#{params}"
receiver.send(stub[:method] || :get,path) { |env|
body = Rack::Utils.parse_nested_query(env.body)
# respond_with takes a model in argument and automatically responds with
# a json representation of the model
# If the action is an update, it attempts to update the model
if model = stub[:respond_with]
model.assign_attributes(body['data']) if stub[:method] == :put && model.respond_to?(:assign_attributes) && body['data']
resp = from_api(model)
else
if stub[:response].is_a?(Proc)
args = stub[:response].arity > 0 ? [body] : []
resp = stub[:response].call(*args)
else
resp = stub[:response] || {}
end
end
# Response code
if stub[:code].is_a?(Proc)
args = stub[:code].arity > 0 ? [body] : []
resp_code = stub[:code].call(*args)
else
resp_code = stub[:code] || 200
end
[resp_code, {}, resp.to_json]
}
end
end
end
end
end