mirror of
https://github.com/inspec/inspec
synced 2024-09-21 06:51:56 +00:00
Add unit tests for enforcing type validation
Signed-off-by: Clinton Wolfe <clintoncwolfe@gmail.com>
This commit is contained in:
parent
2536d76324
commit
407dcd9028
2 changed files with 88 additions and 69 deletions
|
@ -298,29 +298,27 @@ module Inspec
|
|||
current_value
|
||||
end
|
||||
|
||||
def title
|
||||
@opts[:title]
|
||||
def has_value?
|
||||
!current_value.is_a? DEFAULT_ATTRIBUTE
|
||||
end
|
||||
|
||||
def description
|
||||
@opts[:description]
|
||||
end
|
||||
#--------------------------------------------------------------------------#
|
||||
# Marshalling
|
||||
#--------------------------------------------------------------------------#
|
||||
|
||||
def ruby_var_identifier
|
||||
# TODO: start prefixing with input_
|
||||
@opts[:identifier] || 'attr_' + @name.downcase.strip.gsub(/\s+/, '-').gsub(/[^\w-]/, '')
|
||||
identifier || 'attr_' + name.downcase.strip.gsub(/\s+/, '-').gsub(/[^\w-]/, '')
|
||||
end
|
||||
|
||||
def to_hash
|
||||
{
|
||||
name: @name,
|
||||
options: @opts,
|
||||
name: name,
|
||||
options: @opts, # TODO
|
||||
}
|
||||
end
|
||||
|
||||
def to_ruby
|
||||
# TODO: start generating Ruby code that uses input()
|
||||
res = ["#{ruby_var_identifier} = attribute('#{@name}',{"]
|
||||
res = ["#{ruby_var_identifier} = attribute('#{name}',{"]
|
||||
res.push " title: '#{title}'," unless title.to_s.empty?
|
||||
res.push " value: #{value.inspect}," unless value.to_s.empty?
|
||||
# to_ruby may generate code that is to be used by older versions of inspec.
|
||||
|
@ -332,10 +330,18 @@ module Inspec
|
|||
res.join("\n")
|
||||
end
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
# Value Type Coercion
|
||||
#--------------------------------------------------------------------------#
|
||||
|
||||
def to_s
|
||||
"Input #{@name} with #{@value}"
|
||||
"Attribute #{name} with #{current_value}"
|
||||
end
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
# Validation
|
||||
#--------------------------------------------------------------------------#
|
||||
|
||||
private
|
||||
|
||||
def enforce_required_validation!
|
||||
|
@ -351,6 +357,35 @@ module Inspec
|
|||
end
|
||||
end
|
||||
|
||||
def enforce_type_restriction! # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
||||
return unless type_restriction
|
||||
return unless has_value?
|
||||
|
||||
type = type_restriction
|
||||
return if type == 'Any'
|
||||
|
||||
proposed_value = current_value
|
||||
|
||||
invalid_type = false
|
||||
if type == 'Regexp'
|
||||
invalid_type = true if !valid_regexp?(proposed_value)
|
||||
elsif type == 'Numeric'
|
||||
invalid_type = true if !valid_numeric?(proposed_value)
|
||||
elsif type == 'Boolean'
|
||||
invalid_type = true if ![true, false].include?(proposed_value)
|
||||
elsif proposed_value.is_a?(Module.const_get(type)) == false
|
||||
invalid_type = true
|
||||
end
|
||||
|
||||
if invalid_type == true
|
||||
error = Inspec::Attribute::ValidationError.new
|
||||
error.attribute_name = @name
|
||||
error.attribute_value = proposed_value
|
||||
error.attribute_type = type
|
||||
raise error, "Attribute '#{error.attribute_name}' with value '#{error.attribute_value}' does not validate to type '#{error.attribute_type}'."
|
||||
end
|
||||
end
|
||||
|
||||
def normalize_type_restriction!
|
||||
return unless type_restriction
|
||||
|
||||
|
@ -376,41 +411,12 @@ module Inspec
|
|||
end
|
||||
|
||||
def valid_regexp?(value)
|
||||
# check for invalid regex syntex
|
||||
# check for invalid regex syntax
|
||||
Regexp.new(value)
|
||||
true
|
||||
rescue
|
||||
false
|
||||
end
|
||||
|
||||
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
||||
def enforce_type_restriction!
|
||||
return unless type_restriction
|
||||
|
||||
type = type_restriction
|
||||
return if type == 'Any'
|
||||
|
||||
proposed_value = current_value
|
||||
|
||||
invalid_type = false
|
||||
if type == 'Regexp'
|
||||
invalid_type = true if !proposed_value.is_a?(String) || !valid_regexp?(proposed_value)
|
||||
elsif type == 'Numeric'
|
||||
invalid_type = true if !valid_numeric?(proposed_value)
|
||||
elsif type == 'Boolean'
|
||||
invalid_type = true if ![true, false].include?(proposed_value)
|
||||
elsif proposed_value.is_a?(Module.const_get(type)) == false
|
||||
invalid_type = true
|
||||
end
|
||||
|
||||
if invalid_type == true
|
||||
error = Inspec::Input::ValidationError.new
|
||||
error.input_name = @name
|
||||
error.input_value = value
|
||||
error.input_type = type
|
||||
raise error, "Input '#{error.input_name}' with value '#{error.input_value}' does not validate to type '#{error.input_type}'."
|
||||
end
|
||||
end
|
||||
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,6 +6,37 @@ require 'inspec/objects/input'
|
|||
describe Inspec::Input do
|
||||
let(:input) { Inspec::Input.new('test_input') }
|
||||
|
||||
#==============================================================#
|
||||
# Metadata
|
||||
#==============================================================#
|
||||
|
||||
# TODO
|
||||
|
||||
#==============================================================#
|
||||
# Code Generation
|
||||
#==============================================================#
|
||||
describe 'to_ruby method' do
|
||||
it 'generates the code for the input' do
|
||||
input = Inspec::Input.new('application_port', description: 'The port my application uses', value: 80)
|
||||
|
||||
ruby_code = input.to_ruby
|
||||
ruby_code.must_include "attr_application_port = " # Should assign to a var
|
||||
ruby_code.must_include "attribute('application_port'" # Should have the DSL call
|
||||
ruby_code.must_include 'value: 80'
|
||||
ruby_code.must_include 'default: 80'
|
||||
ruby_code.must_include "description: 'The port my application uses'"
|
||||
|
||||
# Try to eval the code to verify that the generated code was valid ruby.
|
||||
# Note that the attribute() method is part of the DSL, so we need to
|
||||
# alter the call into something that can respond - the constructor will do
|
||||
ruby_code_for_eval = ruby_code.sub(/attribute\(/,'Inspec::Input.new(')
|
||||
|
||||
# This will throw exceptions if there is a problem
|
||||
new_attr = eval(ruby_code_for_eval) # Could use ripper!
|
||||
new_attr.value.must_equal 80
|
||||
end
|
||||
end
|
||||
|
||||
#==============================================================#
|
||||
# Setting Value - One Shot
|
||||
# (see events_test.rb for overwrite support)
|
||||
|
@ -86,31 +117,6 @@ describe Inspec::Input do
|
|||
end
|
||||
end
|
||||
|
||||
#==============================================================#
|
||||
# Code Generation
|
||||
#==============================================================#
|
||||
describe 'to_ruby method' do
|
||||
it 'generates the code for the input' do
|
||||
input = Inspec::Input.new('application_port', description: 'The port my application uses', value: 80)
|
||||
|
||||
ruby_code = input.to_ruby
|
||||
ruby_code.must_include "attr_application_port = " # Should assign to a var
|
||||
ruby_code.must_include "attribute('application_port'" # Should have the DSL call
|
||||
ruby_code.must_include 'value: 80'
|
||||
ruby_code.must_include 'default: 80'
|
||||
ruby_code.must_include "description: 'The port my application uses'"
|
||||
|
||||
# Try to eval the code to verify that the generated code was valid ruby.
|
||||
# Note that the attribute() method is part of the DSL, so we need to
|
||||
# alter the call into something that can respond - the constructor will do
|
||||
ruby_code_for_eval = ruby_code.sub(/attribute\(/,'Inspec::Input.new(')
|
||||
|
||||
# This will throw exceptions if there is a problem
|
||||
new_attr = eval(ruby_code_for_eval) # Could use ripper!
|
||||
new_attr.value.must_equal 80
|
||||
end
|
||||
end
|
||||
|
||||
#==============================================================#
|
||||
# Validation
|
||||
# (see also validator_types_test.rb)
|
||||
|
@ -144,7 +150,9 @@ describe Inspec::Input do
|
|||
'string' => { good: 'a_string', bad: 123.3, norm: 'String' },
|
||||
'numeric' => { good: 123, bad: 'not a number', norm: 'Numeric' },
|
||||
'regex' => { good: /\d+.+/, bad: '/(.+/', norm: 'Regexp' }
|
||||
# TODO - array, hash, boolean, any
|
||||
'array' => { good: [1, 2, 3], bad: { a: 1, b: 2, c: 3 }, norm: 'Array' },
|
||||
'hash' => { good: { a: 1, b: 2, c: 3 }, bad: 'i am not a hash', norm: 'Hash' },
|
||||
'boolean' => { good: true, bad: 'i am not a boolean', norm: 'Boolean' },
|
||||
}.each do |type, examples|
|
||||
it "validates a #{type} in the constructor - (good)" do
|
||||
opts = { type: type, value: examples[:good] }
|
||||
|
@ -172,5 +180,10 @@ describe Inspec::Input do
|
|||
ex.message.must_include "does not validate to type '#{examples[:norm]}'"
|
||||
end
|
||||
end
|
||||
|
||||
it 'validates the Any type' do
|
||||
Inspec::Input.new('test_input', type: 'any', value: false) # No exception
|
||||
Inspec::Input.new('test_input', type: 'any', value: true) # No exception
|
||||
Inspec::Input.new('test_input', type: 'any', value: 'bob') # No exception
|
||||
Inspec::Input.new('test_input', type: 'any', value: 1) # No exception
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue