inspec/test/unit/profiles/profile_context_test.rb

436 lines
15 KiB
Ruby
Raw Normal View History

2015-10-17 21:28:54 +00:00
# author: Dominik Richter
# author: Christoph Hartmann
require 'helper'
2015-10-26 03:04:18 +00:00
require 'inspec/profile_context'
2015-10-17 21:28:54 +00:00
class Module
include Minitest::Spec::DSL
end
module DescribeOneTest
it 'loads an empty describe.one' do
profile.load(format(context_format, 'describe.one'))
get_checks.must_equal([])
end
it 'loads an empty describe.one block' do
profile.load(format(context_format, 'describe.one do; end'))
get_checks.must_equal([['describe.one', [], nil]])
end
it 'loads a simple describe.one block' do
profile.load(format(context_format, '
describe.one do
describe true do; it { should eq true }; end
end'))
c = get_checks[0]
c[0].must_equal 'describe.one'
childs = c[1]
childs.length.must_equal 1
childs[0][0].must_equal 'describe'
childs[0][1].must_equal [true]
end
it 'loads a complex describe.one block' do
profile.load(format(context_format, '
describe.one do
describe 0 do; it { should eq true }; end
describe 1 do; it { should eq true }; end
describe 2 do; it { should eq true }; end
end'))
c = get_checks[0]
c[0].must_equal 'describe.one'
childs = c[1]
childs.length.must_equal 3
childs.each_with_index do |ci, idx|
ci[0].must_equal 'describe'
ci[1].must_equal [idx]
end
end
end
2015-10-26 03:04:18 +00:00
describe Inspec::ProfileContext do
2015-10-17 21:28:54 +00:00
let(:backend) { MockLoader.new.backend }
2016-03-08 12:40:34 +00:00
let(:profile) { Inspec::ProfileContext.new(nil, backend, {}) }
2015-10-17 21:28:54 +00:00
def get_checks(rule_index = 0)
rule = profile.rules.values[rule_index]
Inspec::Rule.prepare_checks(rule)
end
2015-10-17 21:28:54 +00:00
it 'must be able to load empty content' do
profile.load('', 'dummy', 1).must_be_nil
end
describe 'its default DSL' do
def load(call)
proc { profile.load(call) }
end
let(:context_format) { '%s' }
include DescribeOneTest
2015-10-17 21:28:54 +00:00
it 'must provide os resource' do
load('print os[:family]').must_output 'debian'
2015-10-17 21:28:54 +00:00
end
it 'must provide file resource' do
load('print file("/etc/passwd").type').must_output 'file'
2015-10-17 21:28:54 +00:00
end
it 'must provide command resource' do
2015-10-17 21:28:54 +00:00
load('print command("").stdout').must_output ''
end
2016-02-25 13:19:29 +00:00
it 'supports empty describe calls' do
load('describe').must_output ''
profile.rules.keys.length.must_equal 1
profile.rules.keys[0].must_match(/^\(generated from \(eval\):1 [0-9a-f]+\)$/)
2016-02-25 13:19:29 +00:00
profile.rules.values[0].must_be_kind_of Inspec::Rule
end
2015-10-17 23:09:47 +00:00
it 'provides the describe keyword in the global DSL' do
2015-10-17 21:28:54 +00:00
load('describe true do; it { should_eq true }; end')
.must_output ''
profile.rules.keys.length.must_equal 1
profile.rules.keys[0].must_match(/^\(generated from \(eval\):1 [0-9a-f]+\)$/)
2015-10-26 03:04:18 +00:00
profile.rules.values[0].must_be_kind_of Inspec::Rule
2015-10-17 21:28:54 +00:00
end
2015-10-17 23:09:47 +00:00
it 'loads multiple computed calls to describe correctly' do
load("%w{1 2 3}.each do\ndescribe true do; it { should_eq true }; end\nend")
.must_output ''
profile.rules.keys.length.must_equal 3
[0, 1, 2].each do |i|
profile.rules.keys[i].must_match(/^\(generated from \(eval\):2 [0-9a-f]+\)$/)
profile.rules.values[i].must_be_kind_of Inspec::Rule
end
end
it 'does not provide the expect keyword in the global DSL' do
2015-10-17 23:09:47 +00:00
load('expect(true).to_eq true').must_raise NoMethodError
end
describe 'global only_if' do
let(:if_true) { "only_if { true }\n" }
let(:if_false) { "only_if { false }\n" }
let(:describe) { "describe nil do its(:to_i) { should eq rand } end\n" }
let(:control) { "control 1 do\n#{describe}\nend\n" }
let(:control_2) { "control 2 do\n#{describe}\nend\n" }
it 'provides the keyword' do
profile.load(if_true)
profile.rules.must_equal({})
end
it 'doesnt affect controls when positive' do
profile.load(if_true + 'control 1')
profile.rules.values[0].must_be_kind_of Inspec::Rule
end
it 'doesnt remove controls when negative' do
profile.load(if_false + 'control 1')
profile.rules.values[0].must_be_kind_of Inspec::Rule
end
it 'alters controls when positive' do
profile.load(if_false + control)
get_checks.length.must_equal 1
get_checks[0][1][0].resource_skipped?.must_equal true
get_checks[0][1][0].resource_exception_message.must_equal 'Skipped control due to only_if condition.'
get_checks[0][1][0].resource_failed?.must_equal false
end
it 'alters non-controls when positive' do
profile.load(if_false + describe)
get_checks.length.must_equal 1
get_checks[0][1][0].resource_skipped?.must_equal true
get_checks[0][1][0].resource_exception_message.must_equal 'Skipped control due to only_if condition.'
get_checks[0][1][0].resource_failed?.must_equal false
end
it 'doesnt alter controls when negative' do
profile.load(if_true + control)
get_checks.length.must_equal 1
get_checks[0][1][0].must_be_nil
end
it 'doesnt alter non-controls when negative' do
profile.load(if_true + describe)
get_checks.length.must_equal 1
get_checks[0][1][0].must_be_nil
end
it 'doesnt overwrite falsy only_ifs' do
profile.load(if_false + if_true + control)
get_checks.length.must_equal 1
get_checks[0][1][0].resource_skipped?.must_equal true
get_checks[0][1][0].resource_exception_message.must_equal 'Skipped control due to only_if condition.'
get_checks[0][1][0].resource_failed?.must_equal false
end
it 'doesnt overwrite falsy only_ifs' do
profile.load(if_true + if_false + control)
get_checks.length.must_equal 1
get_checks[0][1][0].resource_skipped?.must_equal true
get_checks[0][1][0].resource_exception_message.must_equal 'Skipped control due to only_if condition.'
get_checks[0][1][0].resource_failed?.must_equal false
end
it 'allows specifying a message with true only_if' do
profile.load("only_if('this is a only_if skipped message') { false }\n" + control)
get_checks.length.must_equal 1
get_checks[0][1][0].resource_skipped?.must_equal true
get_checks[0][1][0].resource_exception_message.must_equal 'Skipped' \
' control due to only_if condition: this is a only_if skipped message'
get_checks[0][1][0].resource_failed?.must_equal false
end
it 'doesnt extend into other control files' do
fake_control_file = if_false + control
profile.load_control_file(fake_control_file, '(eval)', nil)
profile.load_control_file(control_2, '(eval)', nil)
first_file_check = get_checks(0)
second_file_check = get_checks(1)
first_file_check[0][1][0].resource_exception_message.must_equal 'Skipped control due to only_if condition.'
second_file_check[0][1][0].must_be_nil
end
it 'applies to the controls above it when at the bottom of the file' do
fake_control_file = control + if_false
profile.load_control_file(fake_control_file, '(eval)', 1)
get_checks[0][1][0].resource_exception_message.must_equal 'Skipped control due to only_if condition.'
end
it 'applies to the controls below it when at the top of the file' do
fake_control_file = if_false + control
profile.load_control_file(fake_control_file, '(eval)', 1)
get_checks[0][1][0].resource_exception_message.must_equal 'Skipped control due to only_if condition.'
end
it 'applies to the controls above and below it when at the middle of the file' do
fake_control_file = control + if_false + control_2
profile.load_control_file(fake_control_file, '(eval)', 1)
check_top = get_checks(0)
check_bottom = get_checks(1)
check_top[0][1][0].resource_exception_message.must_equal 'Skipped control due to only_if condition.'
check_bottom[0][1][0].resource_exception_message.must_equal 'Skipped control due to only_if condition.'
end
it 'applies to the describe blocks above and below it when at the middle of the file' do
fake_control_file = describe + if_false + describe
profile.load_control_file(fake_control_file, '(eval)', 1)
check_top = get_checks(0)
check_bottom = get_checks(1)
check_top[0][1][0].resource_exception_message.must_equal 'Skipped control due to only_if condition.'
check_bottom[0][1][0].resource_exception_message.must_equal 'Skipped control due to only_if condition.'
end
end
it 'provides the control keyword in the global DSL' do
profile.load('control 1')
2016-04-09 18:09:04 +00:00
profile.rules.keys.must_equal ['1']
profile.rules.values[0].must_be_kind_of Inspec::Rule
end
it 'provides the rule keyword in the global DSL (legacy mode)' do
2015-10-17 23:09:47 +00:00
profile.load('rule 1')
2016-04-09 18:09:04 +00:00
profile.rules.keys.must_equal ['1']
2015-10-26 03:04:18 +00:00
profile.rules.values[0].must_be_kind_of Inspec::Rule
2015-10-17 23:09:47 +00:00
end
end
describe 'rule DSL' do
let(:rule_id) { rand.to_s }
let(:context_format) { "rule #{rule_id.inspect} do\n%s\nend" }
def get_rule
profile.rules[rule_id]
end
include DescribeOneTest
2015-10-17 23:09:47 +00:00
it 'doesnt add any checks if none are provided' do
profile.load("rule #{rule_id.inspect}")
rule = profile.rules[rule_id]
::Inspec::Rule.prepare_checks(rule).must_equal([])
2015-10-17 23:09:47 +00:00
end
2016-02-25 13:19:29 +00:00
describe 'supports empty describe blocks' do
it 'doesnt crash, but doesnt add anything either' do
profile.load(format(context_format, 'describe'))
profile.rules.keys.must_include(rule_id)
get_checks.must_equal([])
end
end
2015-10-17 23:09:47 +00:00
describe 'adds a check via describe' do
let(:check) {
2016-02-25 13:19:29 +00:00
profile.load(format(context_format,
"describe(os[:family]) { it { must_equal 'debian' } }"
2016-02-25 13:19:29 +00:00
))
get_checks[0]
2015-10-17 23:09:47 +00:00
}
it 'registers the check with describe' do
check[0].must_equal 'describe'
end
it 'registers the check with the describe argument' do
check[1].must_equal %w{debian}
2015-10-17 23:09:47 +00:00
end
it 'registers the check with the provided proc' do
check[2].must_be_kind_of Proc
end
end
describe 'adds a check via expect' do
let(:check) {
2016-02-25 13:19:29 +00:00
profile.load(format(context_format,
"expect(os[:family]).to eq('debian')"
2016-02-25 13:19:29 +00:00
))
get_checks[0]
}
it 'registers the check with describe' do
check[0].must_equal 'expect'
end
it 'registers the check with the describe argument' do
check[1].must_equal %w{debian}
end
it 'registers the check with the provided proc' do
check[2].must_be_kind_of Inspec::Expect
end
end
describe 'adds a check via describe + expect' do
let(:check) {
2016-02-25 13:19:29 +00:00
profile.load(format(context_format,
"describe 'the actual test' do
expect(os[:family]).to eq('debian')
2016-02-25 13:19:29 +00:00
end"
))
get_checks[0]
}
it 'registers the check with describe' do
check[0].must_equal 'describe'
end
it 'registers the check with the describe argument' do
check[1].must_equal ['the actual test']
end
it 'registers the check with the provided proc' do
check[2].must_be_kind_of Proc
end
end
2016-04-05 15:01:08 +00:00
describe 'with only_if' do
it 'provides the only_if keyword' do
2016-04-05 15:01:08 +00:00
profile.load(format(context_format, 'only_if'))
get_checks.must_equal([])
end
it 'skips with only_if == false' do
profile.load(format(context_format, 'only_if { false }'))
get_checks.length.must_equal 1
get_checks[0][1][0].resource_skipped?.must_equal true
get_checks[0][1][0].resource_exception_message.must_equal 'Skipped control due to only_if condition.'
get_checks[0][1][0].resource_failed?.must_equal false
2016-04-05 15:01:08 +00:00
end
it 'does nothing with only_if == false' do
profile.load(format(context_format, 'only_if { true }'))
get_checks.length.must_equal 0
end
it 'doesnt overwrite falsy only_ifs' do
profile.load(format(context_format, "only_if { false }\nonly_if { true }"))
get_checks.length.must_equal 1
get_checks[0][1][0].resource_skipped?.must_equal true
get_checks[0][1][0].resource_exception_message.must_equal 'Skipped control due to only_if condition.'
get_checks[0][1][0].resource_failed?.must_equal false
end
it 'doesnt overwrite falsy only_ifs' do
profile.load(format(context_format, "only_if { true }\nonly_if { false }"))
get_checks.length.must_equal 1
get_checks[0][1][0].resource_skipped?.must_equal true
get_checks[0][1][0].resource_exception_message.must_equal 'Skipped control due to only_if condition.'
get_checks[0][1][0].resource_failed?.must_equal false
end
2016-04-05 15:01:08 +00:00
end
2015-10-17 21:28:54 +00:00
end
describe 'library loading' do
it 'supports simple ruby require statements' do
# Please note: we do discourage the use of Gems in inspec resources at
# this time. Resources should be well packaged whenever possible.
proc { profile.load('Net::POP3') }.must_raise NameError
profile.load_libraries([['require "net/pop"', 'libraries/a.rb']])
profile.load('Net::POP3').to_s.must_equal 'Net::POP3'
end
it 'supports creating a simple library file (no require)' do
# this test will throw an exception if chaining doesn't work
profile.load_libraries([
['module A; end', 'libraries/a.rb']
])
end
it 'supports loading across the library' do
# this test will throw an exception if chaining doesn't work
profile.load_libraries([
["require 'a'\nA", 'libraries/b.rb'],
['module A; end', 'libraries/a.rb']
])
end
it 'supports chain loading across the library' do
# this test will throw an exception if chaining doesn't work
profile.load_libraries([
["require 'b'\nA", 'libraries/c.rb'],
["require 'a'\nA", 'libraries/b.rb'],
['module A; end', 'libraries/a.rb']
])
end
it 'supports loading a regular ruby gem' do
profile.load_libraries([
["require 'erb'\nERB", 'libraries/a.rb']
])
end
it 'fails if a required gem or lib doesnt exist' do
proc {
profile.load_libraries([
["require 'erbluuuuub'", 'libraries/a.rb']
])
}.must_raise LoadError
end
it 'fails loading if reference error occur' do
proc {
profile.load_libraries([
["require 'a'\nB", 'libraries/b.rb'],
['module A; end', 'libraries/a.rb']
])
}.must_raise NameError
end
it 'fails loading if a reference dependency isnt found' do
proc {
profile.load_libraries([
["require 'a'\nA", 'libraries/b.rb'],
])
}.must_raise LoadError
end
end
2015-10-17 21:28:54 +00:00
end