mirror of
https://github.com/samsonjs/grape_logging.git
synced 2026-04-27 14:57:40 +00:00
port deep parameter logging from Badiapp/grape_logging (#37)
* port deep parameter logging from Badiapp/grape_logging; add some specs * fix initializer for ParameterFilter * PR feedback
This commit is contained in:
parent
48adb780d3
commit
cc35a9ad50
11 changed files with 457 additions and 10 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -7,3 +7,4 @@
|
||||||
/pkg/
|
/pkg/
|
||||||
/spec/reports/
|
/spec/reports/
|
||||||
/tmp/
|
/tmp/
|
||||||
|
.rspec
|
||||||
|
|
|
||||||
|
|
@ -23,4 +23,6 @@ Gem::Specification.new do |spec|
|
||||||
|
|
||||||
spec.add_development_dependency 'bundler', '~> 1.8'
|
spec.add_development_dependency 'bundler', '~> 1.8'
|
||||||
spec.add_development_dependency 'rake', '~> 10.0'
|
spec.add_development_dependency 'rake', '~> 10.0'
|
||||||
|
spec.add_development_dependency 'rspec', '~> 3.5'
|
||||||
|
spec.add_development_dependency 'pry-byebug', '~> 3.4.2'
|
||||||
end
|
end
|
||||||
|
|
@ -12,3 +12,4 @@ require 'grape_logging/reporters/active_support_reporter'
|
||||||
require 'grape_logging/reporters/logger_reporter'
|
require 'grape_logging/reporters/logger_reporter'
|
||||||
require 'grape_logging/timings'
|
require 'grape_logging/timings'
|
||||||
require 'grape_logging/middleware/request_logger'
|
require 'grape_logging/middleware/request_logger'
|
||||||
|
require 'grape_logging/util/parameter_filter'
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,35 @@
|
||||||
module GrapeLogging
|
module GrapeLogging
|
||||||
module Loggers
|
module Loggers
|
||||||
class FilterParameters < GrapeLogging::Loggers::Base
|
class FilterParameters < GrapeLogging::Loggers::Base
|
||||||
def initialize(filter_parameters = nil, replacement = '[FILTERED]')
|
AD_PARAMS = 'action_dispatch.request.parameters'.freeze
|
||||||
|
|
||||||
|
def initialize(filter_parameters = nil, replacement = nil, exceptions = %w(controller action format))
|
||||||
@filter_parameters = filter_parameters || (defined?(Rails.application) ? Rails.application.config.filter_parameters : [])
|
@filter_parameters = filter_parameters || (defined?(Rails.application) ? Rails.application.config.filter_parameters : [])
|
||||||
@replacement = replacement
|
@replacement = replacement || '[FILTERED]'
|
||||||
|
@exceptions = exceptions
|
||||||
end
|
end
|
||||||
|
|
||||||
def parameters(request, _)
|
def parameters(request, _)
|
||||||
{ params: replace_parameters(request.params.clone) }
|
{ params: safe_parameters(request) }
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
def replace_parameters(parameters)
|
|
||||||
@filter_parameters.each do |parameter_name|
|
def parameter_filter
|
||||||
if parameters.key?(parameter_name.to_s)
|
@parameter_filter ||= ParameterFilter.new(@replacement, @filter_parameters)
|
||||||
parameters[parameter_name.to_s] = @replacement
|
end
|
||||||
|
|
||||||
|
def safe_parameters(request)
|
||||||
|
# Now this logger can work also over Rails requests
|
||||||
|
if request.params.empty?
|
||||||
|
clean_parameters(request.env[AD_PARAMS] || {})
|
||||||
|
else
|
||||||
|
clean_parameters(request.params)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
parameters
|
|
||||||
|
def clean_parameters(parameters)
|
||||||
|
parameter_filter.filter(parameters).reject{ |key, _value| @exceptions.include?(key) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
90
lib/grape_logging/util/parameter_filter.rb
Normal file
90
lib/grape_logging/util/parameter_filter.rb
Normal file
|
|
@ -0,0 +1,90 @@
|
||||||
|
if defined?(Rails.application)
|
||||||
|
class ParameterFilter < ActionDispatch::Http::ParameterFilter
|
||||||
|
def initialize(_replacement, filter_parameters)
|
||||||
|
super(filter_parameters)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
#
|
||||||
|
# lifted from https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/http/parameter_filter.rb
|
||||||
|
# we could depend on Rails specifically, but that would us way to hefty!
|
||||||
|
#
|
||||||
|
class ParameterFilter
|
||||||
|
def initialize(replacement, filters = [])
|
||||||
|
@replacement = replacement
|
||||||
|
@filters = filters
|
||||||
|
end
|
||||||
|
|
||||||
|
def filter(params)
|
||||||
|
compiled_filter.call(params)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def compiled_filter
|
||||||
|
@compiled_filter ||= CompiledFilter.compile(@replacement, @filters)
|
||||||
|
end
|
||||||
|
|
||||||
|
class CompiledFilter # :nodoc:
|
||||||
|
def self.compile(replacement, filters)
|
||||||
|
return lambda { |params| params.dup } if filters.empty?
|
||||||
|
|
||||||
|
strings, regexps, blocks = [], [], []
|
||||||
|
|
||||||
|
filters.each do |item|
|
||||||
|
case item
|
||||||
|
when Proc
|
||||||
|
blocks << item
|
||||||
|
when Regexp
|
||||||
|
regexps << item
|
||||||
|
else
|
||||||
|
strings << Regexp.escape(item.to_s)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
deep_regexps, regexps = regexps.partition { |r| r.to_s.include?("\\.".freeze) }
|
||||||
|
deep_strings, strings = strings.partition { |s| s.include?("\\.".freeze) }
|
||||||
|
|
||||||
|
regexps << Regexp.new(strings.join('|'.freeze), true) unless strings.empty?
|
||||||
|
deep_regexps << Regexp.new(deep_strings.join('|'.freeze), true) unless deep_strings.empty?
|
||||||
|
|
||||||
|
new replacement, regexps, deep_regexps, blocks
|
||||||
|
end
|
||||||
|
|
||||||
|
attr_reader :regexps, :deep_regexps, :blocks
|
||||||
|
|
||||||
|
def initialize(replacement, regexps, deep_regexps, blocks)
|
||||||
|
@replacement = replacement
|
||||||
|
@regexps = regexps
|
||||||
|
@deep_regexps = deep_regexps.any? ? deep_regexps : nil
|
||||||
|
@blocks = blocks
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(original_params, parents = [])
|
||||||
|
filtered_params = {}
|
||||||
|
|
||||||
|
original_params.each do |key, value|
|
||||||
|
parents.push(key) if deep_regexps
|
||||||
|
if regexps.any? { |r| key =~ r }
|
||||||
|
value = @replacement
|
||||||
|
elsif deep_regexps && (joined = parents.join('.')) && deep_regexps.any? { |r| joined =~ r }
|
||||||
|
value = @replacement
|
||||||
|
elsif value.is_a?(Hash)
|
||||||
|
value = call(value, parents)
|
||||||
|
elsif value.is_a?(Array)
|
||||||
|
value = value.map { |v| v.is_a?(Hash) ? call(v, parents) : v }
|
||||||
|
elsif blocks.any?
|
||||||
|
key = key.dup if key.duplicable?
|
||||||
|
value = value.dup if value.duplicable?
|
||||||
|
blocks.each { |b| b.call(key, value) }
|
||||||
|
end
|
||||||
|
parents.pop if deep_regexps
|
||||||
|
|
||||||
|
filtered_params[key] = value
|
||||||
|
end
|
||||||
|
|
||||||
|
filtered_params
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
49
spec/lib/grape_logging/loggers/client_env_spec.rb
Normal file
49
spec/lib/grape_logging/loggers/client_env_spec.rb
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
require 'ostruct'
|
||||||
|
|
||||||
|
describe GrapeLogging::Loggers::ClientEnv do
|
||||||
|
let(:ip) { '10.0.0.1' }
|
||||||
|
let(:user_agent) { 'user agent' }
|
||||||
|
let(:forwarded_for) { "forwarded for" }
|
||||||
|
let(:remote_addr) { "remote address" }
|
||||||
|
|
||||||
|
context 'forwarded for' do
|
||||||
|
let(:mock_request) do
|
||||||
|
OpenStruct.new(env: {
|
||||||
|
"HTTP_X_FORWARDED_FOR" => forwarded_for
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'sets the ip key' do
|
||||||
|
expect(subject.parameters(mock_request, nil)).to eq(ip: forwarded_for, ua: nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'prefers the forwarded_for over the remote_addr' do
|
||||||
|
mock_request.env['REMOTE_ADDR'] = remote_addr
|
||||||
|
expect(subject.parameters(mock_request, nil)).to eq(ip: forwarded_for, ua: nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'remote address' do
|
||||||
|
let(:mock_request) do
|
||||||
|
OpenStruct.new(env: {
|
||||||
|
"REMOTE_ADDR" => remote_addr
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'sets the ip key' do
|
||||||
|
expect(subject.parameters(mock_request, nil)).to eq(ip: remote_addr, ua: nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'user agent' do
|
||||||
|
let(:mock_request) do
|
||||||
|
OpenStruct.new(env: {
|
||||||
|
"HTTP_USER_AGENT" => user_agent
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'sets the ua key' do
|
||||||
|
expect(subject.parameters(mock_request, nil)).to eq(ip: nil, ua: user_agent)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
75
spec/lib/grape_logging/loggers/filter_parameters_spec.rb
Normal file
75
spec/lib/grape_logging/loggers/filter_parameters_spec.rb
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
require 'ostruct'
|
||||||
|
|
||||||
|
describe GrapeLogging::Loggers::FilterParameters do
|
||||||
|
let(:filtered_parameters) { %w[one four] }
|
||||||
|
|
||||||
|
let(:mock_request) do
|
||||||
|
OpenStruct.new(params: {
|
||||||
|
this_one: 'this one',
|
||||||
|
that_one: 'one',
|
||||||
|
two: 'two',
|
||||||
|
three: 'three',
|
||||||
|
four: 'four'
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:mock_request_with_deep_nesting) do
|
||||||
|
deep_clone = lambda { Marshal.load Marshal.dump mock_request.params }
|
||||||
|
OpenStruct.new(
|
||||||
|
params: deep_clone.call.merge(
|
||||||
|
five: deep_clone.call.merge(
|
||||||
|
deep_clone.call.merge({six: {seven: 'seven', eight: 'eight', one: 'another one'}})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:subject) do
|
||||||
|
GrapeLogging::Loggers::FilterParameters.new filtered_parameters, replacement
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:replacement) { nil }
|
||||||
|
|
||||||
|
shared_examples 'filtering' do
|
||||||
|
it 'filters out sensitive parameters' do
|
||||||
|
expect(subject.parameters(mock_request, nil)).to eq(params: {
|
||||||
|
this_one: subject.instance_variable_get('@replacement'),
|
||||||
|
that_one: subject.instance_variable_get('@replacement'),
|
||||||
|
two: 'two',
|
||||||
|
three: 'three',
|
||||||
|
four: subject.instance_variable_get('@replacement'),
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'deeply filters out sensitive parameters' do
|
||||||
|
expect(subject.parameters(mock_request_with_deep_nesting, nil)).to eq(params: {
|
||||||
|
this_one: subject.instance_variable_get('@replacement'),
|
||||||
|
that_one: subject.instance_variable_get('@replacement'),
|
||||||
|
two: 'two',
|
||||||
|
three: 'three',
|
||||||
|
four: subject.instance_variable_get('@replacement'),
|
||||||
|
five: {
|
||||||
|
this_one: subject.instance_variable_get('@replacement'),
|
||||||
|
that_one: subject.instance_variable_get('@replacement'),
|
||||||
|
two: 'two',
|
||||||
|
three: 'three',
|
||||||
|
four: subject.instance_variable_get('@replacement'),
|
||||||
|
six: {
|
||||||
|
seven: 'seven',
|
||||||
|
eight: 'eight',
|
||||||
|
one: subject.instance_variable_get('@replacement'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with default replacement' do
|
||||||
|
it_behaves_like 'filtering'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with custom replacement' do
|
||||||
|
let(:replacement) { 'CUSTOM_REPLACEMENT' }
|
||||||
|
it_behaves_like 'filtering'
|
||||||
|
end
|
||||||
|
end
|
||||||
13
spec/lib/grape_logging/loggers/request_headers_spec.rb
Normal file
13
spec/lib/grape_logging/loggers/request_headers_spec.rb
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
require 'ostruct'
|
||||||
|
|
||||||
|
describe GrapeLogging::Loggers::RequestHeaders do
|
||||||
|
let(:mock_request) do
|
||||||
|
OpenStruct.new(env: {HTTP_REFERER: 'http://example.com', HTTP_ACCEPT: 'text/plain'})
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'strips HTTP_ from the parameter' do
|
||||||
|
expect(subject.parameters(mock_request, nil)).to eq({
|
||||||
|
headers: {'Referer' => 'http://example.com', 'Accept' => 'text/plain'}
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
27
spec/lib/grape_logging/loggers/response_spec.rb
Normal file
27
spec/lib/grape_logging/loggers/response_spec.rb
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
require 'ostruct'
|
||||||
|
|
||||||
|
describe GrapeLogging::Loggers::Response do
|
||||||
|
context 'with a parseable JSON body' do
|
||||||
|
let(:response) do
|
||||||
|
OpenStruct.new(body: [%q{{"one": "two", "three": {"four": 5}}}])
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns an array of parseable JSON objects' do
|
||||||
|
expect(subject.parameters(nil, response)).to eq({
|
||||||
|
response: [response.body.first.dup]
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with a body that is not parseable JSON' do
|
||||||
|
let(:response) do
|
||||||
|
OpenStruct.new(body: "this is a body")
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'just returns the body' do
|
||||||
|
expect(subject.parameters(nil, response)).to eq({
|
||||||
|
response: response.body.dup
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
89
spec/lib/grape_logging/middleware/request_logger_spec.rb
Normal file
89
spec/lib/grape_logging/middleware/request_logger_spec.rb
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
require 'rack'
|
||||||
|
|
||||||
|
describe GrapeLogging::Middleware::RequestLogger do
|
||||||
|
let(:subject) { request.send(request_method, path) }
|
||||||
|
let(:app) { proc{ [status, {} , ['response body']] } }
|
||||||
|
let(:stack) { described_class.new app, options }
|
||||||
|
let(:request) { Rack::MockRequest.new(stack) }
|
||||||
|
let(:options) { {include: [], logger: logger} }
|
||||||
|
let(:logger) { double('logger') }
|
||||||
|
let(:path) { '/' }
|
||||||
|
let(:request_method) { 'get' }
|
||||||
|
let(:status) { 200 }
|
||||||
|
|
||||||
|
it 'logs to the logger' do
|
||||||
|
expect(logger).to receive('info') do |arguments|
|
||||||
|
expect(arguments[:status]).to eq 200
|
||||||
|
expect(arguments[:method]).to eq 'GET'
|
||||||
|
expect(arguments[:params]).to be_empty
|
||||||
|
expect(arguments[:host]).to eq 'example.org'
|
||||||
|
expect(arguments).to have_key :time
|
||||||
|
expect(arguments[:time]).to have_key :total
|
||||||
|
expect(arguments[:time]).to have_key :db
|
||||||
|
expect(arguments[:time]).to have_key :view
|
||||||
|
end
|
||||||
|
subject
|
||||||
|
end
|
||||||
|
|
||||||
|
[301, 404, 500].each do |the_status|
|
||||||
|
context "when the respnse status is #{the_status}" do
|
||||||
|
let(:status) { the_status }
|
||||||
|
it 'should log the correct status code' do
|
||||||
|
expect(logger).to receive('info') do |arguments|
|
||||||
|
expect(arguments[:status]).to eq the_status
|
||||||
|
end
|
||||||
|
subject
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with a nil response' do
|
||||||
|
let(:app) { proc{ [500, {} , nil] } }
|
||||||
|
it 'should log "fail" instead of a status' do
|
||||||
|
expect(Rack::MockResponse).to receive(:new) { nil }
|
||||||
|
expect(logger).to receive('info') do |arguments|
|
||||||
|
expect(arguments[:status]).to eq 'fail'
|
||||||
|
end
|
||||||
|
subject
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'additional_loggers' do
|
||||||
|
before do
|
||||||
|
options[:include] << GrapeLogging::Loggers::RequestHeaders.new
|
||||||
|
options[:include] << GrapeLogging::Loggers::ClientEnv.new
|
||||||
|
options[:include] << GrapeLogging::Loggers::Response.new
|
||||||
|
options[:include] << GrapeLogging::Loggers::FilterParameters.new(["replace_me"])
|
||||||
|
end
|
||||||
|
|
||||||
|
%w[get put post delete options head patch].each do |the_method|
|
||||||
|
let(:request_method) { the_method }
|
||||||
|
context "with HTTP method[#{the_method}]" do
|
||||||
|
it 'should include additional information in the log' do
|
||||||
|
expect(logger).to receive('info') do |arguments|
|
||||||
|
expect(arguments).to have_key :headers
|
||||||
|
expect(arguments).to have_key :ip
|
||||||
|
expect(arguments).to have_key :response
|
||||||
|
end
|
||||||
|
subject
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should filter parameters in the log' do
|
||||||
|
expect(logger).to receive('info') do |arguments|
|
||||||
|
expect(arguments[:params]).to eq(
|
||||||
|
"replace_me" => '[FILTERED]',
|
||||||
|
"replace_me_too" => '[FILTERED]',
|
||||||
|
"cant_touch_this" => 'should see'
|
||||||
|
)
|
||||||
|
end
|
||||||
|
parameters = {
|
||||||
|
'replace_me' => 'should not see',
|
||||||
|
'replace_me_too' => 'should not see',
|
||||||
|
'cant_touch_this' => 'should see'
|
||||||
|
}
|
||||||
|
request.post path, params: parameters
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
88
spec/spec_helper.rb
Normal file
88
spec/spec_helper.rb
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
$:.unshift '.'
|
||||||
|
require 'lib/grape_logging'
|
||||||
|
|
||||||
|
RSpec.configure do |config|
|
||||||
|
# rspec-expectations config goes here. You can use an alternate
|
||||||
|
# assertion/expectation library such as wrong or the stdlib/minitest
|
||||||
|
# assertions if you prefer.
|
||||||
|
config.expect_with :rspec do |expectations|
|
||||||
|
# This option will default to `true` in RSpec 4. It makes the `description`
|
||||||
|
# and `failure_message` of custom matchers include text for helper methods
|
||||||
|
# defined using `chain`, e.g.:
|
||||||
|
# be_bigger_than(2).and_smaller_than(4).description
|
||||||
|
# # => "be bigger than 2 and smaller than 4"
|
||||||
|
# ...rather than:
|
||||||
|
# # => "be bigger than 2"
|
||||||
|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
||||||
|
end
|
||||||
|
|
||||||
|
# rspec-mocks config goes here. You can use an alternate test double
|
||||||
|
# library (such as bogus or mocha) by changing the `mock_with` option here.
|
||||||
|
config.mock_with :rspec do |mocks|
|
||||||
|
# Prevents you from mocking or stubbing a method that does not exist on
|
||||||
|
# a real object. This is generally recommended, and will default to
|
||||||
|
# `true` in RSpec 4.
|
||||||
|
mocks.verify_partial_doubles = true
|
||||||
|
end
|
||||||
|
|
||||||
|
# This option will default to `:apply_to_host_groups` in RSpec 4 (and will
|
||||||
|
# have no way to turn it off -- the option exists only for backwards
|
||||||
|
# compatibility in RSpec 3). It causes shared context metadata to be
|
||||||
|
# inherited by the metadata hash of host groups and examples, rather than
|
||||||
|
# triggering implicit auto-inclusion in groups with matching metadata.
|
||||||
|
config.shared_context_metadata_behavior = :apply_to_host_groups
|
||||||
|
|
||||||
|
# The settings below are suggested to provide a good initial experience
|
||||||
|
# with RSpec, but feel free to customize to your heart's content.
|
||||||
|
=begin
|
||||||
|
# This allows you to limit a spec run to individual examples or groups
|
||||||
|
# you care about by tagging them with `:focus` metadata. When nothing
|
||||||
|
# is tagged with `:focus`, all examples get run. RSpec also provides
|
||||||
|
# aliases for `it`, `describe`, and `context` that include `:focus`
|
||||||
|
# metadata: `fit`, `fdescribe` and `fcontext`, respectively.
|
||||||
|
config.filter_run_when_matching :focus
|
||||||
|
|
||||||
|
# Allows RSpec to persist some state between runs in order to support
|
||||||
|
# the `--only-failures` and `--next-failure` CLI options. We recommend
|
||||||
|
# you configure your source control system to ignore this file.
|
||||||
|
config.example_status_persistence_file_path = "spec/examples.txt"
|
||||||
|
|
||||||
|
# Limits the available syntax to the non-monkey patched syntax that is
|
||||||
|
# recommended. For more details, see:
|
||||||
|
# - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
|
||||||
|
# - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
|
||||||
|
# - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
|
||||||
|
config.disable_monkey_patching!
|
||||||
|
|
||||||
|
# This setting enables warnings. It's recommended, but in some cases may
|
||||||
|
# be too noisy due to issues in dependencies.
|
||||||
|
config.warnings = true
|
||||||
|
|
||||||
|
# Many RSpec users commonly either run the entire suite or an individual
|
||||||
|
# file, and it's useful to allow more verbose output when running an
|
||||||
|
# individual spec file.
|
||||||
|
if config.files_to_run.one?
|
||||||
|
# Use the documentation formatter for detailed output,
|
||||||
|
# unless a formatter has already been configured
|
||||||
|
# (e.g. via a command-line flag).
|
||||||
|
config.default_formatter = 'doc'
|
||||||
|
end
|
||||||
|
|
||||||
|
# Print the 10 slowest examples and example groups at the
|
||||||
|
# end of the spec run, to help surface which specs are running
|
||||||
|
# particularly slow.
|
||||||
|
config.profile_examples = 10
|
||||||
|
|
||||||
|
# Run specs in random order to surface order dependencies. If you find an
|
||||||
|
# order dependency and want to debug it, you can fix the order by providing
|
||||||
|
# the seed, which is printed after each run.
|
||||||
|
# --seed 1234
|
||||||
|
config.order = :random
|
||||||
|
|
||||||
|
# Seed global randomization in this process using the `--seed` CLI option.
|
||||||
|
# Setting this allows you to use `--seed` to deterministically reproduce
|
||||||
|
# test failures related to randomization by passing the same `--seed` value
|
||||||
|
# as the one that triggered the failure.
|
||||||
|
Kernel.srand config.seed
|
||||||
|
=end
|
||||||
|
end
|
||||||
Loading…
Reference in a new issue