fix buggy verification of primitive resource creation

Newly created primitive resources weren't being marked as updated in
real runs, because exists? was being run on an old object.  Furthermore,
the tests didn't catch this because #exists? on any instance of
Pacemaker::Resource::Primitive was being mocked to return true.  The fix
requires re-invoking Pacemaker::CIBObject.from_name after attempting to
create the new primitive, so that cib_object.exists? accurately reflects
whether the creation succeeded.  However, testing this required mocking
Mixlib::Shellout#stdout to first return "" and then secondly a
definition string for the created resource, and unfortunately this
exposed a nasty bug in rspec-mocks:

  https://github.com/rspec/rspec-mocks/issues/559

So we revamp the mocking of Mixlib::Shellout#stdout to use doubles
instead of #expect_any_instance_of.
This commit is contained in:
Adam Spiers 2014-02-07 01:09:55 +00:00
parent d886f00432
commit 162de47f95
7 changed files with 28 additions and 38 deletions

View File

@ -45,6 +45,7 @@ class Chef
action :nothing
end.run_action(:run)
cib_object = Pacemaker::CIBObject.from_name(new_resource.name)
if cib_object.exists?
new_resource.updated_by_last_action(true)
::Chef::Log.info "Successfully configured #{cib_object}"

View File

@ -15,11 +15,7 @@ shared_examples "a CIB object" do
end
it "should be instantiated via Pacemaker::CIBObject.from_name" do
Mixlib::ShellOut.any_instance.stub(:error!)
expect_any_instance_of(Mixlib::ShellOut) \
.to receive(:stdout) \
.and_return(fixture.definition_string)
expect_definitions(fixture.definition_string)
obj = Pacemaker::CIBObject.from_name(fixture.name)
expect_to_match_fixture(obj)
end
@ -30,10 +26,7 @@ shared_examples "a CIB object" do
end
it "should barf if the loaded definition's type is not colocation" do
Mixlib::ShellOut.any_instance.stub(:error!)
expect_any_instance_of(Mixlib::ShellOut) \
.to receive(:stdout) \
.and_return("clone foo blah blah")
expect_definitions("clone foo blah blah")
expect { fixture.load_definition }.to \
raise_error(Pacemaker::CIBObject::TypeMismatch,
"Expected #{object_type} type but loaded definition was type clone")
@ -42,7 +35,7 @@ end
shared_examples "action on non-existent resource" do |action, cmd, expected_error|
it "should not attempt to #{action.to_s} a non-existent resource" do
expect_definition("")
expect_definitions("")
if expected_error
expect { provider.run_action action }.to \
@ -62,18 +55,18 @@ module Chef::RSpec
# "crm configure show" is executed by load_current_resource, and
# again later on for the :create action, to see whether to create or
# modify.
def expect_definition(definition)
Mixlib::ShellOut.any_instance.stub(:run_command)
Mixlib::ShellOut.any_instance.stub(:error!)
expect_any_instance_of(Mixlib::ShellOut) \
.to receive(:stdout) \
.and_return(definition)
def shellout_double(definition)
shellout = double(Mixlib::ShellOut)
shellout.stub(:environment).and_return({})
shellout.stub(:run_command)
shellout.stub(:error!)
expect(shellout).to receive(:stdout).and_return(definition)
shellout
end
def expect_exists(exists)
expect_any_instance_of(cib_object_class) \
.to receive(:exists?) \
.and_return(exists)
def expect_definitions(*definitions)
doubles = definitions.map { |d| shellout_double(d) }
Mixlib::ShellOut.stub(:new).and_return(*doubles)
end
end
end

View File

@ -21,7 +21,7 @@ shared_examples "a runnable resource" do |fixture|
:delete, "crm configure delete #{fixture.name}", nil
it "should not delete a running resource" do
expect_definition(fixture.definition_string)
expect_definitions(fixture.definition_string)
expect_running(true)
expected_error = "Cannot delete running #{fixture}"
@ -34,7 +34,7 @@ shared_examples "a runnable resource" do |fixture|
end
it "should delete a non-running resource" do
expect_definition(fixture.definition_string)
expect_definitions(fixture.definition_string)
expect_running(false)
provider.run_action :delete
@ -52,7 +52,7 @@ shared_examples "a runnable resource" do |fixture|
"Cannot start non-existent #{fixture}"
it "should do nothing to a started resource" do
expect_definition(fixture.definition_string)
expect_definitions(fixture.definition_string)
expect_running(true)
provider.run_action :start
@ -64,7 +64,7 @@ shared_examples "a runnable resource" do |fixture|
it "should start a stopped resource" do
config = fixture.definition_string.sub("Started", "Stopped")
expect_definition(config)
expect_definitions(config)
expect_running(false)
provider.run_action :start
@ -82,7 +82,7 @@ shared_examples "a runnable resource" do |fixture|
"Cannot stop non-existent #{fixture}"
it "should do nothing to a stopped resource" do
expect_definition(fixture.definition_string)
expect_definitions(fixture.definition_string)
expect_running(false)
provider.run_action :stop
@ -93,7 +93,7 @@ shared_examples "a runnable resource" do |fixture|
end
it "should stop a started resource" do
expect_definition(fixture.definition_string)
expect_definitions(fixture.definition_string)
expect_running(true)
provider.run_action :stop

View File

@ -14,10 +14,6 @@ describe Pacemaker::Resource::Primitive do
Chef::RSpec::Pacemaker::Config::KEYSTONE_PRIMITIVE_DEFINITION
}
before(:each) do
Mixlib::ShellOut.any_instance.stub(:run_command)
end
def object_type
'primitive'
end

View File

@ -35,7 +35,7 @@ describe "Chef::Provider::PacemakerColocation" do
def test_modify(expected_cmds)
yield
expect_definition(fixture.definition_string)
expect_definitions(fixture.definition_string)
provider.run_action :create

View File

@ -36,7 +36,7 @@ describe "Chef::Provider::PacemakerGroup" do
def test_modify(expected_cmds)
yield
expect_definition(fixture.definition_string)
expect_definitions(fixture.definition_string)
provider.run_action :create

View File

@ -38,7 +38,7 @@ describe "Chef::Provider::PacemakerPrimitive" do
def test_modify(expected_cmds)
yield
expect_definition(fixture.definition_string)
expect_definitions(fixture.definition_string)
provider.run_action :create
@ -99,10 +99,10 @@ describe "Chef::Provider::PacemakerPrimitive" do
end
it "should create a primitive if it doesn't already exist" do
expect_definition("")
# Later, the :create action calls cib_object_class#exists? to check
# that creation succeeded.
expect_exists(true)
# The first time, Mixlib::ShellOut needs to return an empty definition.
# Then the resource gets created so the second time it needs to return
# the definition used for creation.
expect_definitions("", fixture.definition_string)
provider.run_action :create
@ -113,7 +113,7 @@ describe "Chef::Provider::PacemakerPrimitive" do
it "should barf if the primitive is already defined with the wrong agent" do
existing_agent = "ocf:openstack:something-else"
definition = fixture.definition_string.sub(fixture.agent, existing_agent)
expect_definition(definition)
expect_definitions(definition)
expected_error = \
"Existing #{fixture} has agent '#{existing_agent}' " \