diff options
author | tyler-ball <tyleraball@gmail.com> | 2014-12-15 14:22:35 -0800 |
---|---|---|
committer | tyler-ball <tyleraball@gmail.com> | 2014-12-17 18:52:24 -0800 |
commit | 44fc2faadb2d63c77fb1bfacb973be615a842c42 (patch) | |
tree | b2750a454ee48dedd63bd093156fdefc9e5eb07f | |
parent | 66727a7a3e8e986ca3499e0e8693c332ab309247 (diff) | |
download | chef-44fc2faadb2d63c77fb1bfacb973be615a842c42.tar.gz |
Finishing unit and functional test coverage for audit runner
-rw-r--r-- | Gemfile | 2 | ||||
-rw-r--r-- | lib/chef/audit/runner.rb | 5 | ||||
-rw-r--r-- | spec/functional/audit/runner_spec.rb | 129 | ||||
-rw-r--r-- | spec/support/audit_helper.rb | 3 | ||||
-rw-r--r-- | spec/unit/audit/runner_spec.rb | 124 |
5 files changed, 207 insertions, 56 deletions
@@ -2,6 +2,8 @@ source "https://rubygems.org" gemspec :name => "chef" gem "activesupport", "< 4.0.0", :group => :compat_testing, :platform => "ruby" +# TODO after serverspec stops including their top level DSL we can remove this +gem "serverspec", :git => "https://github.com/tyler-ball/serverspec/" group(:docgen) do gem "yard" diff --git a/lib/chef/audit/runner.rb b/lib/chef/audit/runner.rb index 5d485a8804..1450ef7f61 100644 --- a/lib/chef/audit/runner.rb +++ b/lib/chef/audit/runner.rb @@ -74,9 +74,6 @@ class Chef # prevents Specinfra and Serverspec from modifying the RSpec configuration # used by our spec tests. def require_deps - # TODO: We need to figure out a way to give audits its own configuration - # object. This involves finding a way to load these files w/o them adding - # to the configuration object used by our spec tests. require 'rspec' require 'rspec/its' require 'specinfra' @@ -143,7 +140,7 @@ class Chef # TODO: We may need to be clever and adjust this based on operating # system, or make it configurable. E.g., there is a PowerShell backend, # as well as an SSH backend. - Specinfra.configuration.backend = :exec + Specinfra.configuration.backend = :exec if Specinfra.configuration.backend != :exec end # Iterates through the controls registered to this run_context, builds an diff --git a/spec/functional/audit/runner_spec.rb b/spec/functional/audit/runner_spec.rb index cb998d66aa..8fab332167 100644 --- a/spec/functional/audit/runner_spec.rb +++ b/spec/functional/audit/runner_spec.rb @@ -1,10 +1,25 @@ +# +# Author:: Tyler Ball (<tball@chef.io>) +# Copyright:: Copyright (c) 2014 Chef Software, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + require 'spec_helper' require 'spec/support/audit_helper' require 'chef/audit/runner' -require 'chef/audit/audit_event_proxy' -require 'chef/audit/rspec_formatter' -require 'chef/run_context' -require 'pry' +require 'rspec/support/spec/in_sub_process' ## # This functional test ensures that our runner can be setup to not interfere with existing RSpec @@ -13,68 +28,80 @@ require 'pry' describe Chef::Audit::Runner do - let(:events) { double("events") } - let(:run_context) { instance_double(Chef::RunContext) } - let(:runner) { Chef::Audit::Runner.new(run_context) } - - # This is the first thing that gets called, and determines how the examples are ran - around(:each) do |ex| - Sandboxing.sandboxed { ex.run } - end - - describe "#configure_rspec" do - - it "adds the necessary formatters" do - # We don't expect the events to receive any calls because the AuditEventProxy that was registered from `runner.run` - # only existed in the Configuration object that got removed by the sandboxing - #expect(events).to receive(:control_example_success) - - expect(RSpec.configuration.formatters.size).to eq(0) - expect(run_context).to receive(:events).and_return(events) - expect(Chef::Audit::AuditEventProxy).to receive(:events=) + # The functional tests must be run in a sub_process. Including Serverspec includes the Serverspec DSL - this + # conflicts with our `package` DSL (among others) when we try to test `package` inside an RSpec example. + # Our DSL leverages `method_missing` while the Serverspec DSL defines a method on the RSpec::Core::ExampleGroup. + # The defined method wins our and returns before our `method_missing` DSL can be called. + # + # Running in a sub_process means the serverspec libraries will only be included in a forked process, not the main one. + include RSpec::Support::InSubProcess - runner.send(:add_formatters) + let(:events) { double("events").as_null_object } + let(:runner) { Chef::Audit::Runner.new(run_context) } + let(:stdout) { StringIO.new } - expect(RSpec.configuration.formatters.size).to eq(2) - expect(RSpec.configuration.formatters[0]).to be_instance_of(Chef::Audit::AuditEventProxy) - expect(RSpec.configuration.formatters[1]).to be_instance_of(Chef::Audit::RspecFormatter) + around(:each) do |ex| + Sandboxing.sandboxed { ex.run } + end + before do + Chef::Config[:log_location] = stdout end - end + # When running these, because we are not mocking out any of the formatters we expect to get dual output on the + # command line + describe "#run" do - # When running these, because we are not mocking out any of the formatters we expect to get dual output on the - # command line - describe "#run" do + let(:audits) { {} } + let(:run_context) { instance_double(Chef::RunContext, :events => events, :audits => audits) } + let(:controls_name) { "controls_name" } - before do - expect(run_context).to receive(:events).and_return(events) - end + it "Correctly runs an empty controls block" do + in_sub_process do + runner.run + end + end - it "Correctly runs an empty controls block" do - expect(run_context).to receive(:audits).and_return({}) - runner.run - end + context "there is a single successful control" do + let(:audits) do + should_pass = lambda do + it "should pass" do + expect(2 - 2).to eq(0) + end + end + { controls_name => Struct.new(:args, :block).new([controls_name], should_pass)} + end - it "Correctly runs a single successful control" do - should_pass = lambda do - it "should pass" do - expect(2 - 2).to eq(0) + it "correctly runs" do + in_sub_process do + runner.run + + expect(stdout.string).to match(/1 example, 0 failures/) + end end end - expect(run_context).to receive(:audits).and_return({ - "should pass" => {:args => [], :block => should_pass} - }) + context "there is a single failing control" do + let(:audits) do + should_fail = lambda do + it "should fail" do + expect(2 - 1).to eq(0) + end + end + { controls_name => Struct.new(:args, :block).new([controls_name], should_fail)} + end - # TODO capture the output and verify it - runner.run - end + it "correctly runs" do + in_sub_process do + runner.run - it "Correctly runs a single failing control", :pending do + expect(stdout.string).to match(/Failure\/Error: expect\(2 - 1\)\.to eq\(0\)/) + expect(stdout.string).to match(/1 example, 1 failure/) + expect(stdout.string).to match(/# controls_name should fail/) + end + end + end end - end - end diff --git a/spec/support/audit_helper.rb b/spec/support/audit_helper.rb index d897de0453..5744f779fc 100644 --- a/spec/support/audit_helper.rb +++ b/spec/support/audit_helper.rb @@ -8,7 +8,8 @@ # rspec-core did not include a license on Github -# This is necessary, otherwise +# Adding these as writers is necessary, otherwise we cannot set the new configuration. +# Only want to do this in the specs. class << RSpec attr_writer :configuration, :world end diff --git a/spec/unit/audit/runner_spec.rb b/spec/unit/audit/runner_spec.rb new file mode 100644 index 0000000000..ca6f51f9eb --- /dev/null +++ b/spec/unit/audit/runner_spec.rb @@ -0,0 +1,124 @@ +# +# Author:: Tyler Ball (<tball@chef.io>) +# Copyright:: Copyright (c) 2014 Chef Software, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'spec_helper' +require 'spec/support/audit_helper' +require 'chef/audit/runner' + +describe Chef::Audit::Runner do + + let(:events) { double("events") } + let(:run_context) { instance_double(Chef::RunContext, :events => events) } + let(:runner) { Chef::Audit::Runner.new(run_context) } + + around(:each) do |ex| + Sandboxing.sandboxed { ex.run } + end + + describe "#initialize" do + it "correctly sets the run_context during initialization" do + expect(runner.instance_variable_get(:@run_context)).to eq(run_context) + end + end + + context "during #run" do + + describe "#setup" do + let(:log_location) { File.join(Dir.tmpdir, 'audit_log') } + let(:color) { false } + + before do + Chef::Config[:log_location] = log_location + Chef::Config[:color] = color + end + + it "sets all the config values" do + runner.send(:setup) + + expect(RSpec.configuration.output_stream).to eq(log_location) + expect(RSpec.configuration.error_stream).to eq(log_location) + + expect(RSpec.configuration.formatters.size).to eq(2) + expect(RSpec.configuration.formatters).to include(instance_of(Chef::Audit::AuditEventProxy)) + expect(RSpec.configuration.formatters).to include(instance_of(Chef::Audit::RspecFormatter)) + expect(Chef::Audit::AuditEventProxy.class_variable_get(:@@events)).to eq(run_context.events) + + expect(RSpec.configuration.expectation_frameworks).to eq([RSpec::Matchers]) + expect(RSpec::Matchers.configuration.syntax).to eq([:expect]) + + expect(RSpec.configuration.color).to eq(color) + expect(RSpec.configuration.expose_dsl_globally?).to eq(false) + + expect(Specinfra.configuration.backend).to eq(:exec) + end + end + + describe "#register_controls" do + let(:audits) { [] } + let(:run_context) { instance_double(Chef::RunContext, :audits => audits) } + + it "adds the control group aliases" do + runner.send(:register_controls) + + expect(RSpec::Core::DSL.example_group_aliases).to include(:__controls__) + expect(RSpec::Core::DSL.example_group_aliases).to include(:control) + end + + context "audits exist" do + let(:audits) { {"audit_name" => group} } + let(:group) {Struct.new(:args, :block).new(["group_name"], nil)} + + it "sends the audits to the world" do + runner.send(:register_controls) + + expect(RSpec.world.example_groups.size).to eq(1) + # For whatever reason, `kind_of` is not working + g = RSpec.world.example_groups[0] + expect(g.ancestors).to include(RSpec::Core::ExampleGroup) + expect(g.description).to eq("group_name") + end + end + end + + describe "#do_run" do + let(:rspec_runner) { instance_double(RSpec::Core::Runner) } + + it "executes the runner" do + expect(RSpec::Core::Runner).to receive(:new).with(nil).and_return(rspec_runner) + expect(rspec_runner).to receive(:run_specs).with([]) + + runner.send(:do_run) + end + end + end + + describe "counters" do + it "correctly calculates failed?" do + expect(runner.failed?).to eq(false) + end + + it "correctly calculates num_failed" do + expect(runner.num_failed).to eq(0) + end + + it "correctly calculates num_total" do + expect(runner.num_total).to eq(0) + end + end + +end |