diff options
Diffstat (limited to 'spec/unit/data_collector_spec.rb')
-rw-r--r-- | spec/unit/data_collector_spec.rb | 518 |
1 files changed, 518 insertions, 0 deletions
diff --git a/spec/unit/data_collector_spec.rb b/spec/unit/data_collector_spec.rb new file mode 100644 index 0000000000..e4f3feb503 --- /dev/null +++ b/spec/unit/data_collector_spec.rb @@ -0,0 +1,518 @@ +# +# Author:: Adam Leff (<adamleff@chef.io) +# Author:: Ryan Cragun (<ryan@chef.io>) +# +# Copyright:: Copyright 2012-2016, 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 "chef/data_collector" + +describe Chef::DataCollector do + describe ".register_reporter?" do + context "when no data collector URL is configured" do + it "returns false" do + Chef::Config[:data_collector_server_url] = nil + expect(Chef::DataCollector.register_reporter?).to be_falsey + end + end + + context "when a data collector URL is configured" do + before do + Chef::Config[:data_collector_server_url] = "http://data_collector" + end + + context "when operating in why_run mode" do + it "returns false" do + Chef::Config[:why_run] = true + expect(Chef::DataCollector.register_reporter?).to be_falsey + end + end + + context "when not operating in why_run mode" do + before do + Chef::Config[:why_run] = false + end + + context "when report is enabled for current mode" do + it "returns true" do + allow(Chef::DataCollector).to receive(:reporter_enabled_for_current_mode?).and_return(true) + expect(Chef::DataCollector.register_reporter?).to be_truthy + end + end + + context "when report is disabled for current mode" do + it "returns false" do + allow(Chef::DataCollector).to receive(:reporter_enabled_for_current_mode?).and_return(false) + expect(Chef::DataCollector.register_reporter?).to be_falsey + end + end + end + end + end + + describe ".reporter_enabled_for_current_mode?" do + context "when running in solo mode" do + before do + Chef::Config[:solo] = true + Chef::Config[:local_mode] = false + end + + context "when data_collector_mode is :solo" do + it "returns true" do + Chef::Config[:data_collector_mode] = :solo + expect(Chef::DataCollector.reporter_enabled_for_current_mode?).to eq(true) + end + end + + context "when data_collector_mode is :client" do + it "returns false" do + Chef::Config[:data_collector_mode] = :client + expect(Chef::DataCollector.reporter_enabled_for_current_mode?).to eq(false) + end + end + + context "when data_collector_mode is :both" do + it "returns true" do + Chef::Config[:data_collector_mode] = :both + expect(Chef::DataCollector.reporter_enabled_for_current_mode?).to eq(true) + end + end + end + + context "when running in local mode" do + before do + Chef::Config[:solo] = false + Chef::Config[:local_mode] = true + end + + context "when data_collector_mode is :solo" do + it "returns true" do + Chef::Config[:data_collector_mode] = :solo + expect(Chef::DataCollector.reporter_enabled_for_current_mode?).to eq(true) + end + end + + context "when data_collector_mode is :client" do + it "returns false" do + Chef::Config[:data_collector_mode] = :client + expect(Chef::DataCollector.reporter_enabled_for_current_mode?).to eq(false) + end + end + + context "when data_collector_mode is :both" do + it "returns true" do + Chef::Config[:data_collector_mode] = :both + expect(Chef::DataCollector.reporter_enabled_for_current_mode?).to eq(true) + end + end + end + + context "when running in client mode" do + before do + Chef::Config[:solo] = false + Chef::Config[:local_mode] = false + end + + context "when data_collector_mode is :solo" do + it "returns false" do + Chef::Config[:data_collector_mode] = :solo + expect(Chef::DataCollector.reporter_enabled_for_current_mode?).to eq(false) + end + end + + context "when data_collector_mode is :client" do + it "returns true" do + Chef::Config[:data_collector_mode] = :client + expect(Chef::DataCollector.reporter_enabled_for_current_mode?).to eq(true) + end + end + + context "when data_collector_mode is :both" do + it "returns true" do + Chef::Config[:data_collector_mode] = :both + expect(Chef::DataCollector.reporter_enabled_for_current_mode?).to eq(true) + end + end + end + end +end + +describe Chef::DataCollector::Reporter do + let(:reporter) { described_class.new } + + describe '#run_started' do + before do + allow(reporter).to receive(:update_run_status) + allow(reporter).to receive(:send_to_data_collector) + end + + it "updates the run status" do + expect(reporter).to receive(:update_run_status).with("test_run_status") + reporter.run_started("test_run_status") + end + + it "sends the RunStart serializer output to the Data Collector server" do + expect(Chef::DataCollector::Serializers::RunStart).to receive(:new).and_return("run_start_data") + expect(reporter).to receive(:send_to_data_collector).with("run_start_data") + reporter.run_started("test_run_status") + end + end + + describe '#run_completed' do + it 'sends the run completion' do + expect(reporter).to receive(:send_run_completion) + reporter.run_completed("fake_node") + end + end + + describe '#run_failed' do + it "updates the exception and sends the run completion" do + expect(reporter).to receive(:update_exception).with("test_exception") + expect(reporter).to receive(:send_run_completion) + reporter.run_failed("test_exception") + end + end + + describe '#resource_current_state_loaded' do + let(:new_resource) { double("new_resource") } + let(:action) { double("action") } + let(:current_resource) { double("current_resource") } + + context "when resource is a nested resource" do + it "does not update the resource report" do + allow(reporter).to receive(:nested_resource?).and_return(true) + expect(reporter).not_to receive(:update_current_resource_report) + reporter.resource_current_state_loaded(new_resource, action, current_resource) + end + end + + context "when resource is not a nested resource" do + it "updates the resource report" do + allow(reporter).to receive(:nested_resource?).and_return(false) + expect(Chef::DataCollector::ResourceReport).to receive(:new).with( + new_resource, + action, + current_resource) + .and_return("resource_report") + expect(reporter).to receive(:update_current_resource_report).with("resource_report") + reporter.resource_current_state_loaded(new_resource, action, current_resource) + end + end + end + + describe '#resource_up_to_date' do + let(:new_resource) { double("new_resource") } + let(:action) { double("action") } + let(:resource_report) { double("resource_report")} + + before do + allow(reporter).to receive(:increment_resource_count) + allow(reporter).to receive(:nested_resource?) + allow(reporter).to receive(:current_resource_report).and_return(resource_report) + allow(resource_report).to receive(:up_to_date) + end + + it "increments the resource count" do + expect(reporter).to receive(:increment_resource_count) + reporter.resource_up_to_date(new_resource, action) + end + + context "when the resource is a nested resource" do + it "does not mark the resource report as up-to-date" do + allow(reporter).to receive(:nested_resource?).with(new_resource).and_return(true) + expect(resource_report).not_to receive(:up_to_date) + reporter.resource_up_to_date(new_resource, action) + end + end + + context "when the resource is not a nested resource" do + it "marks the resource report as up-to-date" do + allow(reporter).to receive(:nested_resource?).with(new_resource).and_return(false) + expect(resource_report).to receive(:up_to_date) + reporter.resource_up_to_date(new_resource, action) + end + end + end + + describe '#resource_skipped' do + let(:new_resource) { double("new_resource") } + let(:action) { double("action") } + let(:conditional) { double("conditional") } + let(:resource_report) { double("resource_report")} + + before do + allow(reporter).to receive(:increment_resource_count) + allow(reporter).to receive(:nested_resource?) + allow(reporter).to receive(:current_resource_report).and_return(resource_report) + allow(resource_report).to receive(:skipped) + end + + it "increments the resource count" do + expect(reporter).to receive(:increment_resource_count) + reporter.resource_skipped(new_resource, action, conditional) + end + + context "when the resource is a nested resource" do + it "does not mark the resource report as skipped" do + allow(reporter).to receive(:nested_resource?).with(new_resource).and_return(true) + expect(resource_report).not_to receive(:skipped).with(conditional) + reporter.resource_skipped(new_resource, action, conditional) + end + end + + context "when the resource is not a nested resource" do + it "updates the resource report" do + allow(reporter).to receive(:nested_resource?).and_return(false) + expect(Chef::DataCollector::ResourceReport).to receive(:new).with( + new_resource, + action) + .and_return("resource_report") + expect(reporter).to receive(:update_current_resource_report).with("resource_report") + reporter.resource_skipped(new_resource, action, conditional) + end + + it "marks the resource report as skipped" do + allow(reporter).to receive(:nested_resource?).with(new_resource).and_return(false) + expect(resource_report).to receive(:skipped).with(conditional) + reporter.resource_skipped(new_resource, action, conditional) + end + end + end + + describe '#resource_updated' do + let(:resource_report) { double("resource_report")} + + before do + allow(reporter).to receive(:current_resource_report).and_return(resource_report) + allow(resource_report).to receive(:updated) + end + + it "increments the resource count" do + expect(reporter).to receive(:increment_resource_count) + reporter.resource_updated("new_resource", "action") + end + + it "marks the resource report as updated" do + expect(resource_report).to receive(:updated) + reporter.resource_updated("new_resource", "action") + end + end + + describe '#resource_failed' do + let(:new_resource) { double("new_resource") } + let(:action) { double("action") } + let(:exception) { double("exception") } + let(:error_mapper) { double("error_mapper")} + let(:resource_report) { double("resource_report")} + + + before do + allow(reporter).to receive(:increment_resource_count) + allow(reporter).to receive(:update_error_description) + allow(reporter).to receive(:current_resource_report).and_return(resource_report) + allow(resource_report).to receive(:failed) + allow(Chef::Formatters::ErrorMapper).to receive(:resource_failed).and_return(error_mapper) + allow(error_mapper).to receive(:for_json) + end + + it "increments the resource count" do + expect(reporter).to receive(:increment_resource_count) + reporter.resource_failed(new_resource, action, exception) + end + + it "updates the error description" do + expect(Chef::Formatters::ErrorMapper).to receive(:resource_failed).with( + new_resource, + action, + exception + ).and_return(error_mapper) + expect(error_mapper).to receive(:for_json).and_return("error_description") + expect(reporter).to receive(:update_error_description).with("error_description") + reporter.resource_failed(new_resource, action, exception) + end + + context "when the resource is not a nested resource" do + it "marks the resource report as failed" do + allow(reporter).to receive(:nested_resource?).with(new_resource).and_return(false) + expect(resource_report).to receive(:failed).with(exception) + reporter.resource_failed(new_resource, action, exception) + end + end + + context "when the resource is a nested resource" do + it "does not mark the resource report as failed" do + allow(reporter).to receive(:nested_resource?).with(new_resource).and_return(true) + expect(resource_report).not_to receive(:failed).with(exception) + reporter.resource_failed(new_resource, action, exception) + end + end + end + + describe '#resource_completed' do + let(:new_resource) { double("new_resource") } + let(:resource_report) { double("resource_report") } + + before do + allow(reporter).to receive(:add_updated_resource) + allow(reporter).to receive(:update_current_resource_report) + allow(resource_report).to receive(:finish) + end + + context "when there is no current resource report" do + it "does not add the updated resource" do + allow(reporter).to receive(:current_resource_report).and_return(nil) + expect(reporter).not_to receive(:add_updated_resource) + reporter.resource_completed(new_resource) + end + end + + context "when there is a current resource report" do + before do + allow(reporter).to receive(:current_resource_report).and_return(resource_report) + end + + context "when the resource is a nested resource" do + it "does not add the updated resource" do + allow(reporter).to receive(:nested_resource?).with(new_resource).and_return(true) + expect(reporter).not_to receive(:add_updated_resource) + reporter.resource_completed(new_resource) + end + end + + context "when the resource is not a nested resource" do + before do + allow(reporter).to receive(:nested_resource?).with(new_resource).and_return(false) + end + + it "marks the current resource report as finished" do + expect(resource_report).to receive(:finish) + reporter.resource_completed(new_resource) + end + + it "adds the resource to the updated resource list" do + expect(reporter).to receive(:add_updated_resource).with(resource_report) + reporter.resource_completed(new_resource) + end + + it "nils out the current resource report" do + expect(reporter).to receive(:update_current_resource_report).with(nil) + reporter.resource_completed(new_resource) + end + end + end + end + + describe '#run_list_expanded' do + it "sets the expanded run list" do + reporter.run_list_expanded("test_run_list") + expect(reporter.expanded_run_list).to eq("test_run_list") + end + end + + describe '#run_list_expand_failed' do + let(:node) { double("node") } + let(:error_mapper) { double("error_mapper") } + let(:exception) { double("exception") } + + it "updates the error description" do + expect(Chef::Formatters::ErrorMapper).to receive(:run_list_expand_failed).with( + node, + exception + ).and_return(error_mapper) + expect(error_mapper).to receive(:for_json).and_return("error_description") + expect(reporter).to receive(:update_error_description).with("error_description") + reporter.run_list_expand_failed(node, exception) + end + end + + describe '#cookbook_resolution_failed' do + let(:error_mapper) { double("error_mapper") } + let(:exception) { double("exception") } + let(:expanded_run_list) { double("expanded_run_list") } + + it "updates the error description" do + expect(Chef::Formatters::ErrorMapper).to receive(:cookbook_resolution_failed).with( + expanded_run_list, + exception + ).and_return(error_mapper) + expect(error_mapper).to receive(:for_json).and_return("error_description") + expect(reporter).to receive(:update_error_description).with("error_description") + reporter.cookbook_resolution_failed(expanded_run_list, exception) + end + + end + + describe '#cookbook_sync_failed' do + let(:cookbooks) { double("cookbooks") } + let(:error_mapper) { double("error_mapper") } + let(:exception) { double("exception") } + + it "updates the error description" do + expect(Chef::Formatters::ErrorMapper).to receive(:cookbook_sync_failed).with( + cookbooks, + exception + ).and_return(error_mapper) + expect(error_mapper).to receive(:for_json).and_return("error_description") + expect(reporter).to receive(:update_error_description).with("error_description") + reporter.cookbook_sync_failed(cookbooks, exception) + end + end + + describe '#disable_reporter_on_error' do + context "when no exception is raise by the block" do + it "does not disable the reporter" do + expect(reporter).not_to receive(:disable_data_collector_reporter) + reporter.send(:disable_reporter_on_error) { true } + end + + it "does not raise an exception" do + expect { reporter.send(:disable_reporter_on_error) { true } }.not_to raise_error + end + end + + context "when an unexpected exception is raised by the block" do + it "re-raises the exception" do + expect { reporter.send(:disable_reporter_on_error) { raise RuntimeError, "bummer" } }.to raise_error(RuntimeError) + end + end + + [ Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, Errno::ECONNREFUSED, EOFError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError ].each do |exception_class| + context "when the block raises a #{exception_class} exception" do + it "disables the reporter" do + expect(reporter).to receive(:disable_data_collector_reporter) + reporter.send(:disable_reporter_on_error) { raise exception_class.new("bummer") } + end + + context "when raise-on-failure is enabled" do + it "logs an error and raises" do + Chef::Config[:data_collector_raise_on_failure] = true + expect(Chef::Log).to receive(:error) + expect { reporter.send(:disable_reporter_on_error) { raise exception_class.new("bummer") } }.to raise_error(exception_class) + end + end + + context "when raise-on-failure is disabled" do + it "logs a warning and does not raise an exception" do + Chef::Config[:data_collector_raise_on_failure] = false + expect(Chef::Log).to receive(:warn) + expect { reporter.send(:disable_reporter_on_error) { raise exception_class.new("bummer") } }.not_to raise_error + end + end + end + end + end +end |