mirror of
https://github.com/inspec/inspec
synced 2024-11-23 21:23:29 +00:00
a5309ea392
Signed-off-by: Ryan Davis <zenspider@chef.io>
434 lines
15 KiB
Ruby
434 lines
15 KiB
Ruby
require "helper"
|
|
require "inspec/profile_context"
|
|
require "inspec/resources/file"
|
|
require "inspec/resources/command"
|
|
|
|
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
|
|
|
|
describe Inspec::ProfileContext do
|
|
let(:backend) { MockLoader.new.backend }
|
|
let(:profile) { Inspec::ProfileContext.new(nil, backend, {}) }
|
|
|
|
def get_checks(rule_index = 0)
|
|
rule = profile.rules.values[rule_index]
|
|
Inspec::Rule.prepare_checks(rule)
|
|
end
|
|
|
|
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
|
|
|
|
it "must provide os resource" do
|
|
load("print os[:family]").must_output "debian"
|
|
end
|
|
|
|
it "must provide file resource" do
|
|
load('print file("/etc/passwd").type').must_output "file"
|
|
end
|
|
|
|
it "must provide command resource" do
|
|
load('print command("").stdout').must_output ""
|
|
end
|
|
|
|
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]+\)$/)
|
|
profile.rules.values[0].must_be_kind_of Inspec::Rule
|
|
end
|
|
|
|
it "provides the describe keyword in the global DSL" do
|
|
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]+\)$/)
|
|
profile.rules.values[0].must_be_kind_of Inspec::Rule
|
|
end
|
|
|
|
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
|
|
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")
|
|
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
|
|
profile.load("rule 1")
|
|
profile.rules.keys.must_equal ["1"]
|
|
profile.rules.values[0].must_be_kind_of Inspec::Rule
|
|
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
|
|
|
|
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([])
|
|
end
|
|
|
|
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
|
|
|
|
describe "adds a check via describe" do
|
|
let(:check) do
|
|
profile.load(format(context_format,
|
|
"describe(os[:family]) { it { must_equal 'debian' } }"
|
|
))
|
|
get_checks[0]
|
|
end
|
|
|
|
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}
|
|
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) do
|
|
profile.load(format(context_format,
|
|
"expect(os[:family]).to eq('debian')"
|
|
))
|
|
get_checks[0]
|
|
end
|
|
|
|
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) do
|
|
profile.load(format(context_format,
|
|
"describe 'the actual test' do
|
|
expect(os[:family]).to eq('debian')
|
|
end"
|
|
))
|
|
get_checks[0]
|
|
end
|
|
|
|
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
|
|
|
|
describe "with only_if" do
|
|
it "provides the only_if keyword" do
|
|
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
|
|
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
|
|
end
|
|
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
|
|
end
|