add and reorganize specs with factory girl

This commit is contained in:
Ryan Buckley 2015-12-11 19:55:05 -08:00
parent 2253c1be19
commit 5959a06cce
8 changed files with 166 additions and 41 deletions

View File

@ -5,4 +5,5 @@ gemspec
group :test do group :test do
gem 'rake' gem 'rake'
gem "factory_girl", "~> 4.0"
end end

View File

@ -16,6 +16,7 @@ gem 'grape-middleware-logger'
## Usage ## Usage
```ruby ```ruby
class API < Grape::API class API < Grape::API
# @note Make sure this above you're first +mount+
use Grape::Middleware::Logger use Grape::Middleware::Logger
end end
``` ```

View File

@ -16,7 +16,7 @@ Gem::Specification.new do |spec|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
spec.require_paths = ['lib'] spec.require_paths = ['lib']
spec.add_dependency 'grape', '>= 0.12', '< 1' spec.add_dependency 'grape', '>= 0.14', '< 1'
spec.add_development_dependency 'bundler', '~> 1.7' spec.add_development_dependency 'bundler', '~> 1.7'
spec.add_development_dependency 'rake', '~> 10.0' spec.add_development_dependency 'rake', '~> 10.0'

View File

@ -16,10 +16,11 @@ class Grape::Middleware::Logger < Grape::Middleware::Globals
super # sets env['grape.*'] super # sets env['grape.*']
logger.info '' logger.info ''
logger.info %Q(Started %s "%s" at %s) % [ logger.info %Q(Started %s "%s" at %s) % [
env['grape.request'].request_method, env[Grape::Env::GRAPE_REQUEST].request_method,
env['grape.request'].path, env[Grape::Env::GRAPE_REQUEST].path,
start_time.to_s start_time.to_s
] ]
logger.info %Q(Processing by #{processed_by})
logger.info %Q( Parameters: #{parameters}) logger.info %Q( Parameters: #{parameters})
end end
@ -73,7 +74,7 @@ class Grape::Middleware::Logger < Grape::Middleware::Globals
end end
def parameters def parameters
request_params = env['grape.request.params'].to_hash request_params = env[Grape::Env::GRAPE_REQUEST_PARAMS].to_hash
request_params.merge!(env['action_dispatch.request.request_parameters'] || {}) # for Rails request_params.merge!(env['action_dispatch.request.request_parameters'] || {}) # for Rails
if @options[:filter] if @options[:filter]
@options[:filter].filter(request_params) @options[:filter].filter(request_params)
@ -86,6 +87,14 @@ class Grape::Middleware::Logger < Grape::Middleware::Globals
@start_time ||= Time.now @start_time ||= Time.now
end end
def processed_by
endpoint = env[Grape::Env::API_ENDPOINT]
parts = endpoint.options[:for].to_s
parts << endpoint.namespace if endpoint.namespace != '/'
parts << '#' << endpoint.options[:path].map { |path| path.to_s.sub('/', '') }.join('/')
parts
end
def default_logger def default_logger
default = Logger.new(STDOUT) default = Logger.new(STDOUT)
default.formatter = LogFormatter.new default.formatter = LogFormatter.new

90
spec/factories.rb Normal file
View File

@ -0,0 +1,90 @@
FactoryGirl.define do
class ExpectedEnv < Hash
attr_accessor :grape_request, :params, :post_params, :grape_endpoint
end
class ParamFilter
def filter(opts)
opts.each_pair { |key, val| val[0..-1] = '[FILTERED]' if key == 'password' }
end
end
class TestAPI
end
class App
attr_accessor :response
def call(_env)
response
end
end
factory :param_filter
require 'rack/rewindable_input'
factory :expected_env do
grape_request { build :grape_request }
params { grape_request.params }
post_params { { 'name' => 'foo', 'password' => 'access' } }
grape_endpoint { build(:grape_endpoint) }
initialize_with do
new.merge(
'REQUEST_METHOD' => 'POST',
'PATH_INFO' => '/api/1.0/users',
'action_dispatch.request.request_parameters' => post_params,
Grape::Env::GRAPE_REQUEST => grape_request,
Grape::Env::GRAPE_REQUEST_PARAMS => params,
Grape::Env::API_ENDPOINT => grape_endpoint
)
end
end
factory :grape_endpoint, class: Grape::Endpoint do
settings { Grape::Util::InheritableSetting.new }
options {
{
path: [:users],
method: 'get',
for: TestAPI
}
}
initialize_with { new(settings, options) }
trait :complex do
options {
{
path: ['/users/:name/profile'],
method: 'put',
for: TestAPI
}
}
end
end
factory :namespaced_endpoint, parent: :grape_endpoint do
initialize_with do
new(settings, options).tap do |me|
me.namespace_stackable(:namespace, Grape::Namespace.new('/admin', {}))
end
end
end
factory :app_response, class: Rack::Response do
initialize_with { new('Hello World', 200, {}) }
end
factory :grape_request, class: OpenStruct do
initialize_with {
new(request_method: 'POST', path: '/api/1.0/users', headers: {}, params: { 'id' => '101001' })
}
end
factory :app do
response { build :app_response }
end
end

View File

@ -0,0 +1,52 @@
require 'spec_helper'
require 'grape/middleware/logger'
describe Grape::Middleware::Logger, type: :integration do
let(:app) { build :app }
let(:options) { { filter: build(:param_filter), logger: Logger.new(Tempfile.new('logger')) } }
subject { described_class.new(app, options) }
let(:app_response) { build :app_response }
let(:grape_request) { build :grape_request }
let(:grape_endpoint) { build(:grape_endpoint) }
let(:env) { build(:expected_env, grape_endpoint: grape_endpoint) }
it 'logs all parts of the request' do
expect(subject.logger).to receive(:info).with ''
expect(subject.logger).to receive(:info).with %Q(Started POST "/api/1.0/users" at #{subject.start_time})
expect(subject.logger).to receive(:info).with %Q(Processing by TestAPI#users)
expect(subject.logger).to receive(:info).with %Q( Parameters: {"id"=>"101001", "name"=>"foo", "password"=>"[FILTERED]"})
expect(subject.logger).to receive(:info).with /Completed 200 in \d.\d+ms/
expect(subject.logger).to receive(:info).with ''
subject.call!(env)
end
describe 'the "processing by" section' do
before { subject.call!(env) }
context 'namespacing' do
let(:grape_endpoint) { build(:namespaced_endpoint) }
it 'designates the namespace with a slash' do
expect(subject.processed_by).to eq 'TestAPI/admin#users'
end
context 'with more complex route' do
let(:grape_endpoint) { build(:namespaced_endpoint, :complex) }
it 'only escapes the first slash and leaves the rest of the untouched' do
expect(subject.processed_by).to eq 'TestAPI/admin#users/:name/profile'
end
end
end
context 'with more complex route' do
let(:grape_endpoint) { build(:grape_endpoint, :complex) }
it 'only escapes the first slash and leaves the rest of the untouched' do
expect(subject.processed_by).to eq 'TestAPI#users/:name/profile'
end
end
end
end

View File

@ -1,25 +1,14 @@
require 'spec_helper' require 'spec_helper'
require 'grape/middleware/logger'
describe Grape::Middleware::Logger do describe Grape::Middleware::Logger do
let(:app) { double('app') } let(:app) { double('app') }
let(:options) { { filter: ParamFilter.new, logger: Object.new } } let(:options) { { filter: build(:param_filter), logger: Object.new } }
subject { described_class.new(app, options) } subject { described_class.new(app, options) }
let(:app_response) { Rack::Response.new 'Hello World', 200, {} } let(:app_response) { build :app_response }
let(:grape_request) { OpenStruct.new(request_method: 'POST', path: '/api/1.0/users', headers: {}, params: { 'id' => '101001' }) } let(:grape_request) { build :grape_request }
let(:env) { let(:env) { build(:expected_env) }
{
'grape.request' => grape_request,
'grape.request.params' => grape_request.params,
'action_dispatch.request.request_parameters' => {
'name' => 'foo',
'password' => 'access'
},
'rack.input' => OpenStruct.new
}
}
describe '#call!' do describe '#call!' do
context 'when calling the app results in an error response' do context 'when calling the app results in an error response' do
@ -158,26 +147,4 @@ describe Grape::Middleware::Logger do
end end
end end
end end
describe 'integration' do
it 'properly logs requests' do
expect(app).to receive(:call).with(env).and_return(app_response)
expect(Grape::Request).to receive(:new).and_return(grape_request)
expect(subject.logger).to receive(:info).with('')
expect(subject.logger).to receive(:info).with(%Q(Started POST "/api/1.0/users" at #{subject.start_time}))
expect(subject.logger).to receive(:info).with(%Q( Parameters: {"id"=>"101001", "name"=>"foo", "password"=>"[FILTERED]"}))
expect(subject.logger).to receive(:info).with(/Completed 200 in \d.\d+ms/)
expect(subject.logger).to receive(:info).with('')
subject.call!(env)
end
end
#
# Test class
#
class ParamFilter
def filter(opts)
opts.each_pair { |key, val| val[0..-1] = '[FILTERED]' if key == 'password' }
end
end
end end

View File

@ -2,7 +2,12 @@ $LOAD_PATH.unshift(File.dirname(__FILE__))
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
require 'ostruct' require 'ostruct'
require 'factory_girl'
require 'grape/middleware/logger'
FactoryGirl.find_definitions
RSpec.configure do |config| RSpec.configure do |config|
config.raise_errors_for_deprecations! config.raise_errors_for_deprecations!
config.include FactoryGirl::Syntax::Methods
end end