diff --git a/CHANGELOG.md b/CHANGELOG.md index d2e9830d9..8cd59a191 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Change log +## [Unreleased] + +* (fbuys) Replace the config schema validator [Kwalify](https://github.com/sunaku/kwalify) with [dry-schema](https://github.com/dry-rb/dry-schema) + ## 6.1.4 (2023-01-13) * (mvz) Update parser dependency to the 3.2.x series diff --git a/docs/How-To-Write-New-Detectors.md b/docs/How-To-Write-New-Detectors.md index aaa91d9a9..0857af5d7 100644 --- a/docs/How-To-Write-New-Detectors.md +++ b/docs/How-To-Write-New-Detectors.md @@ -124,6 +124,18 @@ end The following examples should then cover the detector specific features. +### Schema validation + +We make use of [dry-schema](https://github.com/dry-rb/dry-schema) to validate the +the reek configuration file (usually named `.reek.yml` in the root of a project). +The validation will warn reek users of missing or incorrectly defined configuration. + +If you add or modify a detector you will need to make sure that you update the +[schema](lib/reek/configuration/schema.rb) file. For help with the schema syntax +you can refer to the [dry-schema documentation](https://dry-rb.org/gems/dry-schema). +You can also take a look at the existing schema rules for they all look fairly +similar. + ### Cucumber features We are trying to write as few Cucumber features as possible. diff --git a/features/configuration_files/directory_specific_directives.feature b/features/configuration_files/directory_specific_directives.feature index 77a1e5df2..328fff330 100644 --- a/features/configuration_files/directory_specific_directives.feature +++ b/features/configuration_files/directory_specific_directives.feature @@ -200,7 +200,8 @@ Feature: Directory directives Then the exit status indicates an error And stderr reports: """ - Error: We found some problems with your configuration file: [/directories/dummy_directory/IteratorsNested] key 'IteratorsNested:' is undefined. + Error: Invalid configuration file config.reek, error is + [/directories/dummy_directory/IteratorsNested/enabled] is not allowed. """ Scenario: Abort on file as directory directive diff --git a/features/configuration_files/masking_smells.feature b/features/configuration_files/masking_smells.feature index 032840fd4..026b5235a 100644 --- a/features/configuration_files/masking_smells.feature +++ b/features/configuration_files/masking_smells.feature @@ -9,7 +9,7 @@ Feature: Masking smells using config files When I run reek -c corrupt.reek smelly.rb And stderr reports: """ - Error: We found some problems with your configuration file: [/] 'Not a valid configuration file': not a mapping. + Error: Invalid configuration file corrupt.reek, error is unrecognized configuration data """ And the exit status indicates an error And it reports nothing diff --git a/features/configuration_files/schema_validation.feature b/features/configuration_files/schema_validation.feature index fbacfec1f..0d63aa589 100644 --- a/features/configuration_files/schema_validation.feature +++ b/features/configuration_files/schema_validation.feature @@ -55,5 +55,6 @@ Feature: Validate schema Then the exit status indicates an error And stderr reports: """ - Error: We found some problems with your configuration file: [/detectors/DoesNotExist] key 'DoesNotExist:' is undefined. + Error: Invalid configuration file config.reek, error is + [/detectors/DoesNotExist/enabled] is not allowed. """ diff --git a/lib/reek/configuration/configuration_file_finder.rb b/lib/reek/configuration/configuration_file_finder.rb index 2668d9ed9..b6d6a025e 100644 --- a/lib/reek/configuration/configuration_file_finder.rb +++ b/lib/reek/configuration/configuration_file_finder.rb @@ -58,11 +58,11 @@ def load_from_file(path) begin configuration = YAML.load_file(path) || {} + SchemaValidator.new(configuration).validate rescue StandardError => error raise Errors::ConfigFileError, "Invalid configuration file #{path}, error is #{error}" end - SchemaValidator.new(configuration).validate ConfigurationConverter.new(configuration).convert end diff --git a/lib/reek/configuration/schema.rb b/lib/reek/configuration/schema.rb new file mode 100644 index 000000000..8c09fac52 --- /dev/null +++ b/lib/reek/configuration/schema.rb @@ -0,0 +1,177 @@ +# frozen_string_literal: true + +require 'dry/schema' + +module Reek + module Configuration + # + # Configuration schema constants. + # + class Schema + # Enable the :info extension so we can introspect + # your keys and types + Dry::Schema.load_extensions(:info) + + # rubocop:disable Metrics/BlockLength + ALL_DETECTORS_SCHEMA = Dry::Schema.Params do + optional(:Attribute).filled(:hash) do + optional(:enabled).filled(:bool) + optional(:exclude).array(:string) + end + optional(:BooleanParameter).filled(:hash) do + optional(:enabled).filled(:bool) + optional(:exclude).array(:string) + end + optional(:ClassVariable).filled(:hash) do + optional(:enabled).filled(:bool) + optional(:exclude).array(:string) + end + optional(:ControlParameter).filled(:hash) do + optional(:enabled).filled(:bool) + optional(:exclude).array(:string) + end + optional(:DataClump).filled(:hash) do + optional(:enabled).filled(:bool) + optional(:exclude).array(:string) + optional(:max_copies).filled(:integer) + optional(:min_clump_size).filled(:integer) + end + optional(:DuplicateMethodCall).filled(:hash) do + optional(:enabled).filled(:bool) + optional(:exclude).array(:string) + optional(:max_calls).filled(:integer) + optional(:allow_calls).array(:string) + end + optional(:FeatureEnvy).filled(:hash) do + optional(:enabled).filled(:bool) + optional(:exclude).array(:string) + end + optional(:InstanceVariableAssumption).filled(:hash) do + optional(:enabled).filled(:bool) + optional(:exclude).array(:string) + end + optional(:IrresponsibleModule).filled(:hash) do + optional(:enabled).filled(:bool) + optional(:exclude).array(:string) + end + optional(:LongParameterList).filled(:hash) do + optional(:enabled).filled(:bool) + optional(:exclude).array(:string) + optional(:max_params).filled(:integer) + optional(:overrides).filled(:hash) do + required(:initialize).filled(:hash) do + required(:max_params).filled(:integer) + end + end + end + optional(:LongYieldList).filled(:hash) do + optional(:enabled).filled(:bool) + optional(:exclude).array(:string) + optional(:max_params).filled(:integer) + end + optional(:ManualDispatch).filled(:hash) do + optional(:enabled).filled(:bool) + optional(:exclude).array(:string) + end + optional(:MissingSafeMethod).filled(:hash) do + optional(:enabled).filled(:bool) + optional(:exclude).array(:string) + end + optional(:ModuleInitialize).filled(:hash) do + optional(:enabled).filled(:bool) + optional(:exclude).array(:string) + end + optional(:NestedIterators).filled(:hash) do + optional(:enabled).filled(:bool) + optional(:exclude).array(:string) + optional(:max_allowed_nesting).filled(:integer) + optional(:ignore_iterators) { array(:string) & filled? } + end + optional(:NilCheck).filled(:hash) do + optional(:enabled).filled(:bool) + optional(:exclude).array(:string) + end + optional(:RepeatedConditional).filled(:hash) do + optional(:enabled).filled(:bool) + optional(:exclude).array(:string) + optional(:max_ifs).filled(:integer) + end + optional(:SubclassedFromCoreClass).filled(:hash) do + optional(:enabled).filled(:bool) + optional(:exclude).array(:string) + end + optional(:TooManyConstants).filled(:hash) do + optional(:enabled).filled(:bool) + optional(:exclude).array(:string) + optional(:max_constants).filled(:integer) + end + optional(:TooManyInstanceVariables).filled(:hash) do + optional(:enabled).filled(:bool) + optional(:exclude).array(:string) + optional(:max_instance_variables).filled(:integer) + end + optional(:TooManyMethods).filled(:hash) do + optional(:enabled).filled(:bool) + optional(:exclude).array(:string) + optional(:max_methods).filled(:integer) + end + optional(:TooManyStatements).filled(:hash) do + optional(:enabled).filled(:bool) + optional(:exclude).array(:string) + optional(:max_statements).filled(:integer) + end + optional(:UncommunicativeMethodName).filled(:hash) do + optional(:enabled).filled(:bool) + optional(:exclude).array(:string) + optional(:reject).array(:string) + optional(:accept).array(:string) + end + optional(:UncommunicativeModuleName).filled(:hash) do + optional(:enabled).filled(:bool) + optional(:exclude).array(:string) + optional(:reject).array(:string) + optional(:accept).array(:string) + end + optional(:UncommunicativeParameterName).filled(:hash) do + optional(:enabled).filled(:bool) + optional(:exclude).array(:string) + optional(:reject).array(:string) + optional(:accept).array(:string) + end + optional(:UncommunicativeVariableName).filled(:hash) do + optional(:enabled).filled(:bool) + optional(:exclude).array(:string) + optional(:reject).array(:string) + optional(:accept).array(:string) + end + optional(:UnusedParameters).filled(:hash) do + optional(:enabled).filled(:bool) + optional(:exclude).array(:string) + end + optional(:UnusedPrivateMethod).filled(:hash) do + optional(:enabled).filled(:bool) + optional(:exclude).array(:string) + end + optional(:UtilityFunction).filled(:hash) do + optional(:enabled).filled(:bool) + optional(:exclude).array(:string) + optional(:public_methods_only).filled(:bool) + end + end + # rubocop:enable Metrics/BlockLength + + # @quality :reek:TooManyStatements { max_statements: 7 } + def self.schema(directories = []) + Dry::Schema.Params do + config.validate_keys = true + + optional(:detectors).filled(ALL_DETECTORS_SCHEMA) + optional(:directories).filled(:hash) do + directories.each { |dir| optional(dir.to_sym).filled(ALL_DETECTORS_SCHEMA) } + end + optional(:exclude_paths).array(:string) + end + end + end + end +end diff --git a/lib/reek/configuration/schema.yml b/lib/reek/configuration/schema.yml deleted file mode 100644 index 9b28da121..000000000 --- a/lib/reek/configuration/schema.yml +++ /dev/null @@ -1,210 +0,0 @@ ---- -type: map -mapping: - "detectors": - type: map - mapping: &all_detectors - Attribute: - type: map - mapping: &detector_base - "enabled": - type: bool - "exclude": - type: seq - sequence: - - type: str - BooleanParameter: - type: map - mapping: - <<: *detector_base - ClassVariable: - type: map - mapping: - <<: *detector_base - ControlParameter: - type: map - mapping: - <<: *detector_base - DataClump: - type: map - mapping: - <<: *detector_base - max_copies: - type: number - min_clump_size: - type: number - DuplicateMethodCall: - type: map - mapping: - <<: *detector_base - max_calls: - type: number - allow_calls: - type: seq - sequence: - - type: str - FeatureEnvy: - type: map - mapping: - <<: *detector_base - InstanceVariableAssumption: - type: map - mapping: - <<: *detector_base - IrresponsibleModule: - type: map - mapping: - <<: *detector_base - LongParameterList: - type: map - mapping: - <<: *detector_base - max_params: - type: number - overrides: - type: map - mapping: - initialize: - type: map - mapping: - max_params: - type: number - LongYieldList: - type: map - mapping: - <<: *detector_base - max_params: - type: number - ManualDispatch: - type: map - mapping: - <<: *detector_base - MissingSafeMethod: - type: map - mapping: - <<: *detector_base - ModuleInitialize: - type: map - mapping: - <<: *detector_base - NestedIterators: - type: map - mapping: - <<: *detector_base - max_allowed_nesting: - type: number - ignore_iterators: - type: seq - sequence: - - type: str - NilCheck: - type: map - mapping: - <<: *detector_base - RepeatedConditional: - type: map - mapping: - <<: *detector_base - max_ifs: - type: number - SubclassedFromCoreClass: - type: map - mapping: - <<: *detector_base - Syntax: - type: map - mapping: - <<: *detector_base - TooManyConstants: - type: map - mapping: - <<: *detector_base - max_constants: - type: number - TooManyInstanceVariables: - type: map - mapping: - <<: *detector_base - max_instance_variables: - type: number - TooManyMethods: - type: map - mapping: - <<: *detector_base - max_methods: - type: number - TooManyStatements: - type: map - mapping: - <<: *detector_base - max_statements: - type: number - UncommunicativeMethodName: - type: map - mapping: - <<: *detector_base - reject: &reject_settings - type: seq - sequence: - - type: str - accept: &accept_settings - type: seq - sequence: - - type: str - UncommunicativeModuleName: - type: map - mapping: - <<: *detector_base - reject: *reject_settings - accept: *accept_settings - UncommunicativeParameterName: - type: map - mapping: - <<: *detector_base - reject: *reject_settings - accept: *accept_settings - UncommunicativeVariableName: - type: map - mapping: - <<: *detector_base - reject: *reject_settings - accept: *accept_settings - UnusedParameters: - type: map - mapping: - <<: *detector_base - UnusedPrivateMethod: - type: map - mapping: - <<: *detector_base - UtilityFunction: - type: map - mapping: - <<: *detector_base - public_methods_only: - type: bool - - "directories": - type: map - mapping: - # For any given key that is not matched somewhere else we'll apply the schema below. - # So this will just slurp in every directory we throw at it and then validate everything under - # it against all detectors - for instance: - # - # directories: - # "web_app/app/controllers": - # NestedIterators: - # enabled: false - # "web_app/app/helpers": - # UtilityFunction: - # enabled: false - # - # For details check out: http://www.kuwata-lab.com/kwalify/ruby/users-guide.02.html#tips-default - =: - type: map - mapping: *all_detectors - - "exclude_paths": - type: seq - sequence: - - type: str diff --git a/lib/reek/configuration/schema_validator.rb b/lib/reek/configuration/schema_validator.rb index 3f51585f8..9406eeb48 100644 --- a/lib/reek/configuration/schema_validator.rb +++ b/lib/reek/configuration/schema_validator.rb @@ -1,9 +1,7 @@ # frozen_string_literal: true -require 'yaml' -require_relative '../cli/silencer' -Reek::CLI::Silencer.without_warnings { require 'kwalify' } require_relative '../errors/config_file_error' +require_relative 'schema' module Reek module Configuration @@ -11,28 +9,29 @@ module Configuration # Schema validator module. # class SchemaValidator - SCHEMA_FILE_PATH = File.expand_path('./schema.yml', __dir__) - def initialize(configuration) @configuration = configuration - @validator = CLI::Silencer.without_warnings do - schema_file = Kwalify::Yaml.load_file(SCHEMA_FILE_PATH) - Kwalify::Validator.new(schema_file) - end + config_directories = configuration['directories']&.keys || [] + @validator = Reek::Configuration::Schema.schema(config_directories) end def validate - errors = CLI::Silencer.without_warnings { @validator.validate @configuration } - return if !errors || errors.empty? + result = @validator.call(@configuration) + return if result.success? - raise Errors::ConfigFileError, error_message(errors) + raise Errors::ConfigFileError, error_message(result.errors) + rescue NoMethodError + raise Errors::ConfigFileError, 'unrecognized configuration data' end private # :reek:UtilityFunction def error_message(errors) - "We found some problems with your configuration file: #{CLI::Silencer.silently { errors.join(', ') }}" + messages = errors.map do |error| + "[/#{error.path.join('/')}] #{error.text}." + end.join("\n") + "\n#{messages}" end end end diff --git a/reek.gemspec b/reek.gemspec index 12477afa0..b2604dc69 100644 --- a/reek.gemspec +++ b/reek.gemspec @@ -31,7 +31,7 @@ Gem::Specification.new do |spec| 'rubygems_mfa_required' => 'true' } - spec.add_runtime_dependency 'kwalify', '~> 0.7.0' + spec.add_runtime_dependency 'dry-schema', '~> 1.13.0' spec.add_runtime_dependency 'parser', '~> 3.2.0' spec.add_runtime_dependency 'rainbow', '>= 2.0', '< 4.0' spec.add_runtime_dependency 'rexml', '~> 3.1' diff --git a/spec/reek/configuration/configuration_file_finder_spec.rb b/spec/reek/configuration/configuration_file_finder_spec.rb index 4ff3887c3..bdfa99b33 100644 --- a/spec/reek/configuration/configuration_file_finder_spec.rb +++ b/spec/reek/configuration/configuration_file_finder_spec.rb @@ -120,6 +120,12 @@ end end + it 'raises an error for unrecogized configuration data' do + path = CONFIGURATION_DIR.join('corrupt.reek') + expect { described_class.load_from_file(path) }. + to raise_error(Reek::Errors::ConfigFileError, /Invalid configuration file #{path}/) + end + context 'with exclude, accept and reject settings' do context 'when configuring top level detectors' do let(:configuration) do diff --git a/spec/reek/configuration/schema_spec.rb b/spec/reek/configuration/schema_spec.rb new file mode 100644 index 000000000..392d76126 --- /dev/null +++ b/spec/reek/configuration/schema_spec.rb @@ -0,0 +1,150 @@ +require_relative '../../spec_helper' +require_lib 'reek/configuration/schema' + +RSpec.shared_examples_for 'all detectors present' do |path| + it 'contains a rule for the Attribute detector' do + expect(schema.info.dig(*(path + [:Attribute]))).not_to be_nil + end + + it 'contains a rule for the BooleanParameter detector' do + expect(schema.info.dig(*(path + [:BooleanParameter]))).not_to be_nil + end + + it 'contains a rule for the ClassVariable detector' do + expect(schema.info.dig(*(path + [:ClassVariable]))).not_to be_nil + end + + it 'contains a rule for the ControlParameter detector' do + expect(schema.info.dig(*(path + [:ControlParameter]))).not_to be_nil + end + + it 'contains a rule for the DataClump detector' do + expect(schema.info.dig(*(path + [:DataClump]))).not_to be_nil + end + + it 'contains a rule for the DuplicateMethodCall detector' do + expect(schema.info.dig(*(path + [:DuplicateMethodCall]))).not_to be_nil + end + + it 'contains a rule for the FeatureEnvy detector' do + expect(schema.info.dig(*(path + [:FeatureEnvy]))).not_to be_nil + end + + it 'contains a rule for the InstanceVariableAssumption detector' do + expect(schema.info.dig(*(path + [:InstanceVariableAssumption]))).not_to be_nil + end + + it 'contains a rule for the IrresponsibleModule detector' do + expect(schema.info.dig(*(path + [:IrresponsibleModule]))).not_to be_nil + end + + it 'contains a rule for the LongParameterList detector' do + expect(schema.info.dig(*(path + [:LongParameterList]))).not_to be_nil + end + + it 'contains a rule for the LongYieldList detector' do + expect(schema.info.dig(*(path + [:LongYieldList]))).not_to be_nil + end + + it 'contains a rule for the ManualDispatch detector' do + expect(schema.info.dig(*(path + [:ManualDispatch]))).not_to be_nil + end + + it 'contains a rule for the MissingSafeMethod detector' do + expect(schema.info.dig(*(path + [:MissingSafeMethod]))).not_to be_nil + end + + it 'contains a rule for the ModuleInitialize detector' do + expect(schema.info.dig(*(path + [:ModuleInitialize]))).not_to be_nil + end + + it 'contains a rule for the NestedIterators detector' do + expect(schema.info.dig(*(path + [:NestedIterators]))).not_to be_nil + end + + it 'contains a rule for the NilCheck detector' do + expect(schema.info.dig(*(path + [:NilCheck]))).not_to be_nil + end + + it 'contains a rule for the RepeatedConditional detector' do + expect(schema.info.dig(*(path + [:RepeatedConditional]))).not_to be_nil + end + + it 'contains a rule for the SubclassedFromCoreClass detector' do + expect(schema.info.dig(*(path + [:SubclassedFromCoreClass]))).not_to be_nil + end + + it 'contains a rule for the TooManyConstants detector' do + expect(schema.info.dig(*(path + [:TooManyConstants]))).not_to be_nil + end + + it 'contains a rule for the TooManyInstanceVariables detector' do + expect(schema.info.dig(*(path + [:TooManyInstanceVariables]))).not_to be_nil + end + + it 'contains a rule for the TooManyMethods detector' do + expect(schema.info.dig(*(path + [:TooManyMethods]))).not_to be_nil + end + + it 'contains a rule for the TooManyStatements detector' do + expect(schema.info.dig(*(path + [:TooManyStatements]))).not_to be_nil + end + + it 'contains a rule for the UncommunicativeMethodName detector' do + expect(schema.info.dig(*(path + [:UncommunicativeMethodName]))).not_to be_nil + end + + it 'contains a rule for the UncommunicativeModuleName detector' do + expect(schema.info.dig(*(path + [:UncommunicativeModuleName]))).not_to be_nil + end + + it 'contains a rule for the UncommunicativeParameterName detector' do + expect(schema.info.dig(*(path + [:UncommunicativeParameterName]))).not_to be_nil + end + + it 'contains a rule for the UncommunicativeVariableName detector' do + expect(schema.info.dig(*(path + [:UncommunicativeVariableName]))).not_to be_nil + end + + it 'contains a rule for the UnusedParameters detector' do + expect(schema.info.dig(*(path + [:UnusedParameters]))).not_to be_nil + end + + it 'contains a rule for the UnusedPrivateMethod detector' do + expect(schema.info.dig(*(path + [:UnusedPrivateMethod]))).not_to be_nil + end + + it 'contains a rule for the UtilityFunction detector' do + expect(schema.info.dig(*(path + [:UtilityFunction]))).not_to be_nil + end +end + +RSpec.describe Reek::Configuration::Schema do + describe '#schema' do + let(:schema) { described_class.schema } + + it 'contains rules for detectors' do + expect(schema.info[:keys]).to include(Reek::DETECTORS_KEY.to_sym) + end + + it_behaves_like 'all detectors present', [:keys, Reek::DETECTORS_KEY.to_sym, :keys] + + it 'contains rules for directories' do + expect(schema.info[:keys]).to include(Reek::DIRECTORIES_KEY.to_sym) + end + + it 'contains rules for exclude_paths' do + expect(schema.info[:keys]).to include(Reek::EXCLUDE_PATHS_KEY.to_sym) + end + + it 'does not create unnecessary directories rules' do + expect(schema.info.dig(:keys, :directories, :keys)).to be_empty + end + + context 'with directories' do + let(:schema) { described_class.schema(['app/controllers']) } + + it_behaves_like 'all detectors present', [:keys, Reek::DIRECTORIES_KEY.to_sym, :keys, :'app/controllers', :keys] + end + end +end diff --git a/spec/reek/configuration/schema_validator_spec.rb b/spec/reek/configuration/schema_validator_spec.rb index 5fef753bb..2d2e63e37 100644 --- a/spec/reek/configuration/schema_validator_spec.rb +++ b/spec/reek/configuration/schema_validator_spec.rb @@ -21,6 +21,15 @@ end end + context 'when configuration is invalid and unparsable' do + let(:configuration) { 'Not a valid configuration file' } + + it 'raises an error' do + message = 'unrecognized configuration data' + expect { validator.validate }.to raise_error(Reek::Errors::ConfigFileError, message) + end + end + context 'when detector is invalid' do let(:configuration) do { @@ -31,7 +40,7 @@ end it 'raises an error' do - message = %r{\[/detectors/DoesNotExist\] key 'DoesNotExist:' is undefined} + message = %r{\[/detectors/DoesNotExist/enabled\] is not allowed.} expect { validator.validate }.to raise_error(Reek::Errors::ConfigFileError, message) end end @@ -46,7 +55,7 @@ end it 'raises an error' do - message = %r{\[/detectors/FeatureEnvy/enabled\] 'foo': not a boolean} + message = %r{\[/detectors/FeatureEnvy/enabled\] must be boolean.} expect { validator.validate }.to raise_error(Reek::Errors::ConfigFileError, message) end end @@ -61,7 +70,7 @@ end it 'raises an error' do - message = %r{\[/detectors/DataClump/does_not_exist\] key 'does_not_exist:' is undefined} + message = %r{\[/detectors/DataClump/does_not_exist\] is not allowed.} expect { validator.validate }.to raise_error(Reek::Errors::ConfigFileError, message) end end @@ -78,7 +87,8 @@ end it 'raises an error' do - message = %r{\[/detectors/UncommunicativeMethodName/#{attribute}\] '42': not a sequence} + message = %r{\n\[/detectors/UncommunicativeMethodName/#{attribute}\] must be an array.} + expect { validator.validate }.to raise_error(Reek::Errors::ConfigFileError, message) end end @@ -93,7 +103,8 @@ end it 'raises an error' do - message = %r{\[/detectors/UncommunicativeMethodName/#{attribute}/0\] '42': not a string} + message = %r{\n\[/detectors/UncommunicativeMethodName/#{attribute}/0\] must be a string.} + expect { validator.validate }.to raise_error(Reek::Errors::ConfigFileError, message) end end @@ -108,7 +119,7 @@ end it 'raises an error' do - message = %r{\[/exclude_paths\] '42': not a sequence} + message = %r{\[/exclude_paths\] must be an array.} expect { validator.validate }.to raise_error(Reek::Errors::ConfigFileError, message) end end @@ -121,7 +132,7 @@ end it 'raises an error' do - message = %r{\[/exclude_paths/0\] '42': not a string} + message = %r{\[/exclude_paths/0\] must be a string.} expect { validator.validate }.to raise_error(Reek::Errors::ConfigFileError, message) end end @@ -139,7 +150,7 @@ end it 'raises an error' do - message = %r{\[/directories/web_app/app/helpers/Bar\] key 'Bar:' is undefined} + message = %r{\[/directories/web_app/app/helpers/Bar/enabled\] is not allowed.} expect { validator.validate }.to raise_error(Reek::Errors::ConfigFileError, message) end end @@ -156,7 +167,7 @@ end it 'raises an error' do - message = %r{\[/directories/web_app/app/controllers/NestedIterators/foo\] key 'foo:' is undefined} + message = %r{\[/directories/web_app/app/controllers/NestedIterators/foo\] is not allowed.} expect { validator.validate }.to raise_error(Reek::Errors::ConfigFileError, message) end end