File Manager
Viewing File: state_spec.rb
require "support/shared/integration/integration_helper"
describe "Chef::Resource#identity and #state" do
include IntegrationSupport
class NewResourceNamer
@i = 0
def self.next
"chef_resource_property_spec_#{@i += 1}"
end
end
def self.new_resource_name
NewResourceNamer.next
end
let(:resource_class) do
new_resource_name = self.class.new_resource_name
Class.new(Chef::Resource) do
resource_name new_resource_name
end
end
let(:resource) do
resource_class.new("blah")
end
def self.english_join(values)
return "<nothing>" if values.size == 0
return values[0].inspect if values.size == 1
"#{values[0..-2].map { |v| v.inspect }.join(", ")} and #{values[-1].inspect}"
end
def self.with_property(*properties, &block)
tags_index = properties.find_index { |p| !p.is_a?(String) }
if tags_index
properties, tags = properties[0..tags_index - 1], properties[tags_index..-1]
else
tags = []
end
properties = properties.map { |property| "property #{property}" }
context "With properties #{english_join(properties)}", *tags do
before do
properties.each do |property_str|
resource_class.class_eval(property_str, __FILE__, __LINE__)
end
end
instance_eval(&block)
end
end
# identity
context "Chef::Resource#identity_properties" do
with_property ":x" do
it "name is the default identity" do
expect(resource_class.identity_properties).to eq [ Chef::Resource.properties[:name] ]
expect(Chef::Resource.properties[:name].identity?).to be_falsey
expect(resource.name).to eq "blah"
expect(resource.identity).to eq "blah"
end
it "identity_properties :x changes the identity" do
expect(resource_class.identity_properties :x).to eq [ resource_class.properties[:x] ]
expect(resource_class.identity_properties).to eq [ resource_class.properties[:x] ]
expect(Chef::Resource.properties[:name].identity?).to be_falsey
expect(resource_class.properties[:x].identity?).to be_truthy
expect(resource.x "woo").to eq "woo"
expect(resource.x).to eq "woo"
expect(resource.name).to eq "blah"
expect(resource.identity).to eq "woo"
end
with_property ":y, identity: true" do
context "and identity_properties :x" do
before do
resource_class.class_eval do
identity_properties :x
end
end
it "only returns :x as identity" do
resource.x "foo"
resource.y "bar"
expect(resource_class.identity_properties).to eq [ resource_class.properties[:x] ]
expect(resource.identity).to eq "foo"
end
it "does not flip y.desired_state off" do
resource.x "foo"
resource.y "bar"
expect(resource_class.state_properties).to eq [
resource_class.properties[:x],
resource_class.properties[:y],
]
expect(resource.state_for_resource_reporter).to eq(x: "foo", y: "bar")
end
end
end
context "With a subclass" do
let(:subresource_class) do
new_resource_name = self.class.new_resource_name
Class.new(resource_class) do
resource_name new_resource_name
end
end
let(:subresource) do
subresource_class.new("sub")
end
it "name is the default identity on the subclass" do
expect(subresource_class.identity_properties).to eq [ Chef::Resource.properties[:name] ]
expect(Chef::Resource.properties[:name].identity?).to be_falsey
expect(subresource.name).to eq "sub"
expect(subresource.identity).to eq "sub"
end
context "With identity_properties :x on the superclass" do
before do
resource_class.class_eval do
identity_properties :x
end
end
it "The subclass inherits :x as identity" do
expect(subresource_class.identity_properties).to eq [ subresource_class.properties[:x] ]
expect(Chef::Resource.properties[:name].identity?).to be_falsey
expect(subresource_class.properties[:x].identity?).to be_truthy
subresource.x "foo"
expect(subresource.identity).to eq "foo"
end
context "With property :y, identity: true on the subclass" do
before do
subresource_class.class_eval do
property :y, identity: true
end
end
it "The subclass's identity includes both x and y" do
expect(subresource_class.identity_properties).to eq [
subresource_class.properties[:x],
subresource_class.properties[:y],
]
subresource.x "foo"
subresource.y "bar"
expect(subresource.identity).to eq(x: "foo", y: "bar")
end
end
with_property ":y, String" do
context "With identity_properties :y on the subclass" do
before do
subresource_class.class_eval do
identity_properties :y
end
end
it "y is part of state" do
subresource.x "foo"
subresource.y "bar"
expect(subresource.state_for_resource_reporter).to eq(x: "foo", y: "bar")
expect(subresource_class.state_properties).to eq [
subresource_class.properties[:x],
subresource_class.properties[:y],
]
end
it "y is the identity" do
expect(subresource_class.identity_properties).to eq [ subresource_class.properties[:y] ]
subresource.x "foo"
subresource.y "bar"
expect(subresource.identity).to eq "bar"
end
it "y still has validation" do
expect { subresource.y 12 }.to raise_error Chef::Exceptions::ValidationFailed
end
end
end
end
end
end
with_property ":string_only, String, identity: true", ":string_only2, String" do
it "identity_properties does not change validation" do
resource_class.identity_properties :string_only
expect { resource.string_only 12 }.to raise_error Chef::Exceptions::ValidationFailed
expect { resource.string_only2 12 }.to raise_error Chef::Exceptions::ValidationFailed
end
end
with_property ":x, desired_state: false" do
it "identity_properties does not change desired_state" do
resource_class.identity_properties :x
resource.x "hi"
expect(resource.identity).to eq "hi"
expect(resource_class.properties[:x].desired_state?).to be_falsey
expect(resource_class.state_properties).to eq []
expect(resource.state_for_resource_reporter).to eq({})
end
end
context "With custom property custom_property defined only as methods, using different variables for storage" do
before do
resource_class.class_eval do
def custom_property
@blarghle ? @blarghle * 3 : nil
end
def custom_property=(x)
@blarghle = x * 2
end
end
end
context "And identity_properties :custom_property" do
before do
resource_class.class_eval do
identity_properties :custom_property
end
end
it "identity_properties comes back as :custom_property" do
expect(resource_class.properties[:custom_property].identity?).to be_truthy
expect(resource_class.identity_properties).to eq [ resource_class.properties[:custom_property] ]
end
it "custom_property becomes part of desired_state" do
resource.custom_property = 1
expect(resource.state_for_resource_reporter).to eq(custom_property: 6)
expect(resource_class.properties[:custom_property].desired_state?).to be_truthy
expect(resource_class.state_properties).to eq [
resource_class.properties[:custom_property],
]
end
it "identity_properties does not change custom_property's getter or setter" do
resource.custom_property = 1
expect(resource.custom_property).to eq 6
end
it "custom_property is returned as the identity" do
expect(resource.identity).to be_nil
resource.custom_property = 1
expect(resource.identity).to eq 6
end
end
end
end
context "Property#identity" do
with_property ":x, identity: true" do
it "name is only part of the identity if an identity attribute is defined" do
expect(resource_class.identity_properties).to eq [ resource_class.properties[:x] ]
resource.x "woo"
expect(resource.identity).to eq "woo"
end
end
with_property ":x, identity: true, default: 'xxx'",
":y, identity: true, default: 'yyy'",
":z, identity: true, default: 'zzz'" do
it "identity_property raises an error if multiple identity values are defined" do
expect { resource_class.identity_property }.to raise_error Chef::Exceptions::MultipleIdentityError
end
it "identity_attr raises an error if multiple identity values are defined" do
expect { resource_class.identity_attr }.to raise_error Chef::Exceptions::MultipleIdentityError
end
it "identity returns all identity values in a hash if multiple are defined" do
resource.x "foo"
resource.y "bar"
resource.z "baz"
expect(resource.identity).to eq(x: "foo", y: "bar", z: "baz")
end
it "identity returns all values whether any value is set or not" do
expect(resource.identity).to eq(x: "xxx", y: "yyy", z: "zzz")
end
it "identity_properties wipes out any other identity attributes if multiple are defined" do
resource_class.identity_properties :y
resource.x "foo"
resource.y "bar"
resource.z "baz"
expect(resource.identity).to eq "bar"
end
end
with_property ":x, identity: true, name_property: true" do
it "identity when x is not defined returns the value of x" do
expect(resource.identity).to eq "blah"
end
it "state when x is not defined returns the value of x" do
expect(resource.state_for_resource_reporter).to eq(x: "blah")
end
end
end
# state_properties
context "Chef::Resource#state_properties" do
it "state_properties is empty by default" do
expect(Chef::Resource.state_properties).to eq []
expect(resource.state_for_resource_reporter).to eq({})
end
with_property ":x", ":y", ":z" do
it "x, y and z are state attributes" do
resource.x 1
resource.y 2
resource.z 3
expect(resource_class.state_properties).to eq [
resource_class.properties[:x],
resource_class.properties[:y],
resource_class.properties[:z],
]
expect(resource.state_for_resource_reporter).to eq(x: 1, y: 2, z: 3)
end
it "values that are not set are not included in state" do
resource.x 1
expect(resource.state_for_resource_reporter).to eq(x: 1)
end
it "when no values are set, nothing is included in state" do
end
end
with_property ":x", ":y, desired_state: false", ":z, desired_state: true" do
it "x and z are state attributes, and y is not" do
resource.x 1
resource.y 2
resource.z 3
expect(resource_class.state_properties).to eq [
resource_class.properties[:x],
resource_class.properties[:z],
]
expect(resource.state_for_resource_reporter).to eq(x: 1, z: 3)
end
end
with_property ":x, name_property: true" do
# it "Unset values with name_property are included in state" do
# expect(resource.state_for_resource_reporter).to eq({ x: 'blah' })
# end
it "Set values with name_property are included in state" do
resource.x 1
expect(resource.state_for_resource_reporter).to eq(x: 1)
end
end
with_property ":x, default: 1" do
it "Unset values with defaults are not included in state" do
expect(resource.state_for_resource_reporter).to eq({})
end
it "Set values with defaults are included in state" do
resource.x 1
expect(resource.state_for_resource_reporter).to eq(x: 1)
end
end
context "With a class with a normal getter and setter" do
before do
resource_class.class_eval do
def x
@blah * 3
end
def x=(value)
@blah = value * 2
end
end
end
it "state_properties(:x) causes the value to be included in properties" do
resource_class.state_properties(:x)
resource.x = 1
expect(resource.x).to eq 6
expect(resource.state_for_resource_reporter).to eq(x: 6)
end
end
context "When state_properties happens before properties are declared" do
before do
resource_class.class_eval do
state_properties :x
property :x
end
end
it "the property works and is in state_properties" do
expect(resource_class.state_properties).to include(resource_class.properties[:x])
resource.x = 1
expect(resource.x).to eq 1
expect(resource.state_for_resource_reporter).to eq(x: 1)
end
end
with_property ":x, Integer, identity: true" do
it "state_properties(:x) leaves the property in desired_state" do
resource_class.state_properties(:x)
resource.x 10
expect(resource_class.properties[:x].desired_state?).to be_truthy
expect(resource_class.state_properties).to eq [
resource_class.properties[:x],
]
expect(resource.state_for_resource_reporter).to eq(x: 10)
end
it "state_properties(:x) does not turn off validation" do
resource_class.state_properties(:x)
expect { resource.x "ouch" }.to raise_error Chef::Exceptions::ValidationFailed
end
it "state_properties(:x) does not turn off identity" do
resource_class.state_properties(:x)
resource.x 10
expect(resource_class.identity_properties).to eq [ resource_class.properties[:x] ]
expect(resource_class.properties[:x].identity?).to be_truthy
expect(resource.identity).to eq 10
end
end
with_property ":x, Integer, identity: true, desired_state: false" do
before do
resource_class.class_eval do
def y
20
end
end
end
it "state_properties(:x) leaves x identical" do
old_value = resource_class.properties[:y]
resource_class.state_properties(:x)
resource.x 10
expect(resource_class.properties[:y].object_id).to eq old_value.object_id
expect(resource_class.properties[:x].desired_state?).to be_truthy
expect(resource_class.properties[:x].identity?).to be_truthy
expect(resource_class.identity_properties).to eq [
resource_class.properties[:x],
]
expect(resource.identity).to eq(10)
expect(resource_class.state_properties).to eq [
resource_class.properties[:x],
]
expect(resource.state_for_resource_reporter).to eq(x: 10)
end
it "state_properties(:y) adds y to desired state" do
old_value = resource_class.properties[:x]
resource_class.state_properties(:y)
resource.x 10
expect(resource_class.properties[:x].object_id).to eq old_value.object_id
expect(resource_class.properties[:x].desired_state?).to be_falsey
expect(resource_class.properties[:y].desired_state?).to be_truthy
expect(resource_class.state_properties).to eq [
resource_class.properties[:y],
]
expect(resource.state_for_resource_reporter).to eq(y: 20)
end
context "With a subclassed resource" do
let(:subresource_class) do
new_resource_name = self.class.new_resource_name
Class.new(resource_class) do
resource_name new_resource_name
end
end
let(:subresource) do
subresource_class.new("blah")
end
it "state_properties(:x) adds x to desired state" do
old_value = resource_class.properties[:y]
subresource_class.state_properties(:x)
subresource.x 10
expect(subresource_class.properties[:y].object_id).to eq old_value.object_id
expect(subresource_class.properties[:x].desired_state?).to be_truthy
expect(subresource_class.properties[:x].identity?).to be_truthy
expect(subresource_class.identity_properties).to eq [
subresource_class.properties[:x],
]
expect(subresource.identity).to eq(10)
expect(subresource_class.state_properties).to eq [
subresource_class.properties[:x],
]
expect(subresource.state_for_resource_reporter).to eq(x: 10)
end
it "state_properties(:y) adds y to desired state" do
old_value = resource_class.properties[:x]
subresource_class.state_properties(:y)
subresource.x 10
expect(subresource_class.properties[:x].object_id).to eq old_value.object_id
expect(subresource_class.properties[:y].desired_state?).to be_truthy
expect(subresource_class.state_properties).to eq [
subresource_class.properties[:y],
]
expect(subresource.state_for_resource_reporter).to eq(y: 20)
expect(subresource_class.properties[:x].identity?).to be_truthy
expect(subresource_class.identity_properties).to eq [
subresource_class.properties[:x],
]
expect(subresource.identity).to eq(10)
end
end
end
end
end