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 action :nothing
end.run_action(:run) end.run_action(:run)
cib_object = Pacemaker::CIBObject.from_name(new_resource.name)
if cib_object.exists? if cib_object.exists?
new_resource.updated_by_last_action(true) new_resource.updated_by_last_action(true)
::Chef::Log.info "Successfully configured #{cib_object}" ::Chef::Log.info "Successfully configured #{cib_object}"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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