chef 12.4.3 → 12.5.0.alpha.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +1 -2
  3. data/lib/chef.rb +1 -1
  4. data/lib/chef/application/solo.rb +1 -1
  5. data/lib/chef/application/windows_service_manager.rb +17 -12
  6. data/lib/chef/chef_class.rb +7 -0
  7. data/lib/chef/chef_fs/config.rb +22 -24
  8. data/lib/chef/chef_fs/file_pattern.rb +4 -15
  9. data/lib/chef/chef_fs/file_system/cookbook_dir.rb +1 -0
  10. data/lib/chef/chef_fs/knife.rb +35 -7
  11. data/lib/chef/chef_fs/path_utils.rb +65 -34
  12. data/lib/chef/constants.rb +27 -0
  13. data/lib/chef/delayed_evaluator.rb +21 -0
  14. data/lib/chef/dsl/recipe.rb +20 -2
  15. data/lib/chef/event_dispatch/base.rb +40 -16
  16. data/lib/chef/event_dispatch/dsl.rb +64 -0
  17. data/lib/chef/exceptions.rb +6 -1
  18. data/lib/chef/formatters/doc.rb +3 -1
  19. data/lib/chef/guard_interpreter/resource_guard_interpreter.rb +3 -1
  20. data/lib/chef/http/http_request.rb +1 -1
  21. data/lib/chef/knife/bootstrap/templates/chef-full.erb +1 -1
  22. data/lib/chef/knife/ssl_check.rb +3 -2
  23. data/lib/chef/knife/user_edit.rb +1 -2
  24. data/lib/chef/mixin/params_validate.rb +362 -135
  25. data/lib/chef/node.rb +19 -0
  26. data/lib/chef/platform/handler_map.rb +0 -5
  27. data/lib/chef/platform/rebooter.rb +1 -1
  28. data/lib/chef/property.rb +539 -0
  29. data/lib/chef/provider.rb +129 -12
  30. data/lib/chef/provider/deploy.rb +3 -5
  31. data/lib/chef/provider/lwrp_base.rb +1 -75
  32. data/lib/chef/provider/package.rb +1 -1
  33. data/lib/chef/provider/powershell_script.rb +32 -19
  34. data/lib/chef/provider/registry_key.rb +5 -5
  35. data/lib/chef/provider/service/macosx.rb +5 -1
  36. data/lib/chef/recipe.rb +1 -8
  37. data/lib/chef/resource.rb +499 -84
  38. data/lib/chef/resource/file/verification.rb +7 -1
  39. data/lib/chef/resource/lwrp_base.rb +1 -7
  40. data/lib/chef/run_context.rb +404 -83
  41. data/lib/chef/version.rb +1 -1
  42. data/lib/chef/win32/registry.rb +10 -2
  43. data/lib/chef/workstation_config_loader.rb +3 -158
  44. data/spec/data/run_context/cookbooks/include/recipes/default.rb +24 -0
  45. data/spec/data/run_context/cookbooks/include/recipes/includee.rb +3 -0
  46. data/spec/functional/rebooter_spec.rb +1 -1
  47. data/spec/functional/resource/{powershell_spec.rb → powershell_script_spec.rb} +3 -3
  48. data/spec/functional/win32/registry_helper_spec.rb +12 -0
  49. data/spec/functional/win32/service_manager_spec.rb +2 -2
  50. data/spec/integration/knife/chef_repo_path_spec.rb +13 -11
  51. data/spec/integration/recipes/recipe_dsl_spec.rb +0 -15
  52. data/spec/integration/recipes/resource_action_spec.rb +343 -0
  53. data/spec/spec_helper.rb +1 -0
  54. data/spec/support/shared/functional/win32_service.rb +2 -1
  55. data/spec/unit/application/solo_spec.rb +4 -3
  56. data/spec/unit/chef_class_spec.rb +23 -0
  57. data/spec/unit/chef_fs/path_util_spec.rb +108 -0
  58. data/spec/unit/event_dispatch/dsl_spec.rb +87 -0
  59. data/spec/unit/json_compat_spec.rb +4 -3
  60. data/spec/unit/knife/ssl_check_spec.rb +4 -0
  61. data/spec/unit/mixin/params_validate_spec.rb +4 -2
  62. data/spec/unit/node_spec.rb +7 -0
  63. data/spec/unit/property/state_spec.rb +506 -0
  64. data/spec/unit/property/validation_spec.rb +658 -0
  65. data/spec/unit/property_spec.rb +968 -0
  66. data/spec/unit/provider/{powershell_spec.rb → powershell_script_spec.rb} +0 -0
  67. data/spec/unit/provider/registry_key_spec.rb +12 -0
  68. data/spec/unit/provider/service/macosx_spec.rb +4 -4
  69. data/spec/unit/provider_spec.rb +1 -3
  70. data/spec/unit/recipe_spec.rb +0 -4
  71. data/spec/unit/registry_helper_spec.rb +15 -1
  72. data/spec/unit/resource/file/verification_spec.rb +33 -5
  73. data/spec/unit/resource/{powershell_spec.rb → powershell_script_spec.rb} +0 -0
  74. data/spec/unit/resource_spec.rb +2 -2
  75. data/spec/unit/run_context/child_run_context_spec.rb +133 -0
  76. data/spec/unit/run_context_spec.rb +7 -0
  77. metadata +25 -25
  78. data/spec/unit/workstation_config_loader_spec.rb +0 -283
@@ -0,0 +1,87 @@
1
+ #
2
+ # Author:: Ranjib Dey (<ranjib@linux.com>)
3
+ #
4
+ # Copyright:: Copyright (c) 2015 Ranjib Dey
5
+ # License:: Apache License, Version 2.0
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+ require 'spec_helper'
20
+ require 'chef/event_dispatch/dsl'
21
+
22
+ describe Chef::EventDispatch::DSL do
23
+ let(:events) do
24
+ Chef::EventDispatch::Dispatcher.new
25
+ end
26
+
27
+ let(:run_context) do
28
+ Chef::RunContext.new(Chef::Node.new, nil, events)
29
+ end
30
+
31
+ before do
32
+ Chef.set_run_context(run_context)
33
+ end
34
+
35
+ after do
36
+ Chef.reset!
37
+ end
38
+
39
+ subject{ described_class.new('test') }
40
+
41
+ it 'set handler name' do
42
+ subject.on(:run_started) {}
43
+ expect(events.subscribers.first.name).to eq('test')
44
+ end
45
+
46
+ it 'raise error when invalid event type is supplied' do
47
+ expect do
48
+ subject.on(:foo_bar) {}
49
+ end.to raise_error(Chef::Exceptions::InvalidEventType)
50
+ end
51
+
52
+ it 'register user hooks against valid event type' do
53
+ subject.on(:run_failed) {'testhook'}
54
+ expect(events.subscribers.first.run_failed).to eq('testhook')
55
+ end
56
+
57
+ it 'preserve state across event hooks' do
58
+ calls = []
59
+ Chef.event_handler do
60
+ on :resource_updated do
61
+ calls << :updated
62
+ end
63
+ on :resource_action_start do
64
+ calls << :started
65
+ end
66
+ end
67
+ resource = Chef::Resource::RubyBlock.new('foo', run_context)
68
+ resource.block { }
69
+ resource.run_action(:run)
70
+ expect(calls).to eq([:started, :updated])
71
+ end
72
+
73
+ it 'preserve instance variables across handler callbacks' do
74
+ Chef.event_handler do
75
+ on :resource_action_start do
76
+ @ivar = [1]
77
+ end
78
+ on :resource_updated do
79
+ @ivar << 2
80
+ end
81
+ end
82
+ resource = Chef::Resource::RubyBlock.new('foo', run_context)
83
+ resource.block { }
84
+ resource.run_action(:run)
85
+ expect(events.subscribers.first.instance_variable_get(:@ivar)).to eq([1, 2])
86
+ end
87
+ end
@@ -72,8 +72,6 @@ describe Chef::JSONCompat do
72
72
  end
73
73
  end
74
74
 
75
- # On FreeBSD 10.1 i386 rspec fails with a SystemStackError loading the expect line with more that 252 entries
76
- # https://github.com/chef/chef/issues/3101
77
75
  describe "with the file with 252 or less nested entries" do
78
76
  let(:json) { IO.read(File.join(CHEF_SPEC_DATA, 'nested.json')) }
79
77
  let(:hash) { Chef::JSONCompat.from_json(json) }
@@ -84,7 +82,10 @@ describe Chef::JSONCompat do
84
82
  end
85
83
 
86
84
  it "should has 'test' as a 252 nested value" do
87
- expect(hash['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']).to eq('test')
85
+ v = 252.times.inject(hash) do |memo, _|
86
+ memo['key']
87
+ end
88
+ expect(v).to eq('test')
88
89
  end
89
90
  end
90
91
  end
@@ -163,6 +163,7 @@ E
163
163
  expect(ssl_check).to receive(:verify_X509).and_return(true) # X509 valid certs (no warn)
164
164
  expect(ssl_socket).to receive(:connect) # no error
165
165
  expect(ssl_socket).to receive(:post_connection_check).with("foo.example.com") # no error
166
+ expect(ssl_socket).to receive(:hostname=).with("foo.example.com") # no error
166
167
  end
167
168
 
168
169
  it "prints a success message" do
@@ -197,6 +198,7 @@ E
197
198
  expect(ssl_socket).to receive(:post_connection_check).
198
199
  with("foo.example.com").
199
200
  and_raise(OpenSSL::SSL::SSLError)
201
+ expect(ssl_socket).to receive(:hostname=).with("foo.example.com") # no error
200
202
  expect(ssl_socket_for_debug).to receive(:connect)
201
203
  expect(ssl_socket_for_debug).to receive(:peer_cert).and_return(self_signed_crt)
202
204
  end
@@ -215,6 +217,8 @@ E
215
217
  expect(ssl_check).to receive(:verify_X509).and_return(true) # X509 valid certs
216
218
  expect(ssl_socket).to receive(:connect).
217
219
  and_raise(OpenSSL::SSL::SSLError)
220
+ expect(ssl_socket).to receive(:hostname=).
221
+ with("foo.example.com") # no error
218
222
  expect(ssl_socket_for_debug).to receive(:connect)
219
223
  expect(ssl_socket_for_debug).to receive(:peer_cert).and_return(self_signed_crt)
220
224
  end
@@ -21,6 +21,8 @@ require 'spec_helper'
21
21
  class TinyClass
22
22
  include Chef::Mixin::ParamsValidate
23
23
 
24
+ attr_reader :name
25
+
24
26
  def music(is_good=true)
25
27
  is_good
26
28
  end
@@ -331,11 +333,11 @@ describe Chef::Mixin::ParamsValidate do
331
333
  it "asserts that a value returns false from a predicate method" do
332
334
  expect do
333
335
  @vo.validate({:not_blank => "should pass"},
334
- {:not_blank => {:cannot_be => :nil, :cannot_be => :empty}})
336
+ {:not_blank => {:cannot_be => [ :nil, :empty ]}})
335
337
  end.not_to raise_error
336
338
  expect do
337
339
  @vo.validate({:not_blank => ""},
338
- {:not_blank => {:cannot_be => :nil, :cannot_be => :empty}})
340
+ {:not_blank => {:cannot_be => [ :nil, :empty ]}})
339
341
  end.to raise_error(Chef::Exceptions::ValidationFailed)
340
342
  end
341
343
 
@@ -672,6 +672,13 @@ describe Chef::Node do
672
672
  expect(node.run_list).to eq([ "role[base]", "recipe[chef::server]" ])
673
673
  end
674
674
 
675
+ it "sets the node chef_environment" do
676
+ attrs = { "chef_environment" => "foo_environment", "bar" => "baz" }
677
+ expect(node.consume_chef_environment(attrs)).to eq({ "bar" => "baz" })
678
+ expect(node.chef_environment).to eq("foo_environment")
679
+ expect(node['chef_environment']).to be nil
680
+ end
681
+
675
682
  it "should overwrites the run list with the run list it consumes" do
676
683
  node.consume_run_list "recipes" => [ "one", "two" ]
677
684
  node.consume_run_list "recipes" => [ "three" ]
@@ -0,0 +1,506 @@
1
+ require 'support/shared/integration/integration_helper'
2
+
3
+ describe "Chef::Resource#identity and #state" do
4
+ include IntegrationSupport
5
+
6
+ class NewResourceNamer
7
+ @i = 0
8
+ def self.next
9
+ "chef_resource_property_spec_#{@i += 1}"
10
+ end
11
+ end
12
+
13
+ def self.new_resource_name
14
+ NewResourceNamer.next
15
+ end
16
+
17
+ let(:resource_class) do
18
+ new_resource_name = self.class.new_resource_name
19
+ Class.new(Chef::Resource) do
20
+ resource_name new_resource_name
21
+ end
22
+ end
23
+
24
+ let(:resource) do
25
+ resource_class.new("blah")
26
+ end
27
+
28
+ def self.english_join(values)
29
+ return '<nothing>' if values.size == 0
30
+ return values[0].inspect if values.size == 1
31
+ "#{values[0..-2].map { |v| v.inspect }.join(", ")} and #{values[-1].inspect}"
32
+ end
33
+
34
+ def self.with_property(*properties, &block)
35
+ tags_index = properties.find_index { |p| !p.is_a?(String)}
36
+ if tags_index
37
+ properties, tags = properties[0..tags_index-1], properties[tags_index..-1]
38
+ else
39
+ tags = []
40
+ end
41
+ properties = properties.map { |property| "property #{property}" }
42
+ context "With properties #{english_join(properties)}", *tags do
43
+ before do
44
+ properties.each do |property_str|
45
+ resource_class.class_eval(property_str, __FILE__, __LINE__)
46
+ end
47
+ end
48
+ instance_eval(&block)
49
+ end
50
+ end
51
+
52
+ # identity
53
+ context "Chef::Resource#identity_properties" do
54
+ with_property ":x" do
55
+ it "name is the default identity" do
56
+ expect(resource_class.identity_properties).to eq [ Chef::Resource.properties[:name] ]
57
+ expect(Chef::Resource.properties[:name].identity?).to be_falsey
58
+ expect(resource.name).to eq 'blah'
59
+ expect(resource.identity).to eq 'blah'
60
+ end
61
+
62
+ it "identity_properties :x changes the identity" do
63
+ expect(resource_class.identity_properties :x).to eq [ resource_class.properties[:x] ]
64
+ expect(resource_class.identity_properties).to eq [ resource_class.properties[:x] ]
65
+ expect(Chef::Resource.properties[:name].identity?).to be_falsey
66
+ expect(resource_class.properties[:x].identity?).to be_truthy
67
+
68
+ expect(resource.x 'woo').to eq 'woo'
69
+ expect(resource.x).to eq 'woo'
70
+
71
+ expect(resource.name).to eq 'blah'
72
+ expect(resource.identity).to eq 'woo'
73
+ end
74
+
75
+ with_property ":y, identity: true" do
76
+ context "and identity_properties :x" do
77
+ before do
78
+ resource_class.class_eval do
79
+ identity_properties :x
80
+ end
81
+ end
82
+
83
+ it "only returns :x as identity" do
84
+ resource.x 'foo'
85
+ resource.y 'bar'
86
+ expect(resource_class.identity_properties).to eq [ resource_class.properties[:x] ]
87
+ expect(resource.identity).to eq 'foo'
88
+ end
89
+ it "does not flip y.desired_state off" do
90
+ resource.x 'foo'
91
+ resource.y 'bar'
92
+ expect(resource_class.state_properties).to eq [
93
+ resource_class.properties[:x],
94
+ resource_class.properties[:y]
95
+ ]
96
+ expect(resource.state_for_resource_reporter).to eq(x: 'foo', y: 'bar')
97
+ end
98
+ end
99
+ end
100
+
101
+ context "With a subclass" do
102
+ let(:subresource_class) do
103
+ new_resource_name = self.class.new_resource_name
104
+ Class.new(resource_class) do
105
+ resource_name new_resource_name
106
+ end
107
+ end
108
+ let(:subresource) do
109
+ subresource_class.new('sub')
110
+ end
111
+
112
+ it "name is the default identity on the subclass" do
113
+ expect(subresource_class.identity_properties).to eq [ Chef::Resource.properties[:name] ]
114
+ expect(Chef::Resource.properties[:name].identity?).to be_falsey
115
+ expect(subresource.name).to eq 'sub'
116
+ expect(subresource.identity).to eq 'sub'
117
+ end
118
+
119
+ context "With identity_properties :x on the superclass" do
120
+ before do
121
+ resource_class.class_eval do
122
+ identity_properties :x
123
+ end
124
+ end
125
+
126
+ it "The subclass inherits :x as identity" do
127
+ expect(subresource_class.identity_properties).to eq [ subresource_class.properties[:x] ]
128
+ expect(Chef::Resource.properties[:name].identity?).to be_falsey
129
+ expect(subresource_class.properties[:x].identity?).to be_truthy
130
+
131
+ subresource.x 'foo'
132
+ expect(subresource.identity).to eq 'foo'
133
+ end
134
+
135
+ context "With property :y, identity: true on the subclass" do
136
+ before do
137
+ subresource_class.class_eval do
138
+ property :y, identity: true
139
+ end
140
+ end
141
+ it "The subclass's identity includes both x and y" do
142
+ expect(subresource_class.identity_properties).to eq [
143
+ subresource_class.properties[:x],
144
+ subresource_class.properties[:y]
145
+ ]
146
+ subresource.x 'foo'
147
+ subresource.y 'bar'
148
+ expect(subresource.identity).to eq(x: 'foo', y: 'bar')
149
+ end
150
+ end
151
+
152
+ with_property ":y, String" do
153
+ context "With identity_properties :y on the subclass" do
154
+ before do
155
+ subresource_class.class_eval do
156
+ identity_properties :y
157
+ end
158
+ end
159
+ it "y is part of state" do
160
+ subresource.x 'foo'
161
+ subresource.y 'bar'
162
+ expect(subresource.state_for_resource_reporter).to eq(x: 'foo', y: 'bar')
163
+ expect(subresource_class.state_properties).to eq [
164
+ subresource_class.properties[:x],
165
+ subresource_class.properties[:y]
166
+ ]
167
+ end
168
+ it "y is the identity" do
169
+ expect(subresource_class.identity_properties).to eq [ subresource_class.properties[:y] ]
170
+ subresource.x 'foo'
171
+ subresource.y 'bar'
172
+ expect(subresource.identity).to eq 'bar'
173
+ end
174
+ it "y still has validation" do
175
+ expect { subresource.y 12 }.to raise_error Chef::Exceptions::ValidationFailed
176
+ end
177
+ end
178
+ end
179
+ end
180
+ end
181
+ end
182
+
183
+ with_property ":string_only, String, identity: true", ":string_only2, String" do
184
+ it "identity_properties does not change validation" do
185
+ resource_class.identity_properties :string_only
186
+ expect { resource.string_only 12 }.to raise_error Chef::Exceptions::ValidationFailed
187
+ expect { resource.string_only2 12 }.to raise_error Chef::Exceptions::ValidationFailed
188
+ end
189
+ end
190
+
191
+ with_property ":x, desired_state: false" do
192
+ it "identity_properties does not change desired_state" do
193
+ resource_class.identity_properties :x
194
+ resource.x 'hi'
195
+ expect(resource.identity).to eq 'hi'
196
+ expect(resource_class.properties[:x].desired_state?).to be_falsey
197
+ expect(resource_class.state_properties).to eq []
198
+ expect(resource.state_for_resource_reporter).to eq({})
199
+ end
200
+ end
201
+
202
+ context "With custom property custom_property defined only as methods, using different variables for storage" do
203
+ before do
204
+ resource_class.class_eval do
205
+ def custom_property
206
+ @blarghle ? @blarghle*3 : nil
207
+ end
208
+ def custom_property=(x)
209
+ @blarghle = x*2
210
+ end
211
+ end
212
+ end
213
+
214
+ context "And identity_properties :custom_property" do
215
+ before do
216
+ resource_class.class_eval do
217
+ identity_properties :custom_property
218
+ end
219
+ end
220
+
221
+ it "identity_properties comes back as :custom_property" do
222
+ expect(resource_class.properties[:custom_property].identity?).to be_truthy
223
+ expect(resource_class.identity_properties).to eq [ resource_class.properties[:custom_property] ]
224
+ end
225
+ it "custom_property becomes part of desired_state" do
226
+ resource.custom_property = 1
227
+ expect(resource.state_for_resource_reporter).to eq(custom_property: 6)
228
+ expect(resource_class.properties[:custom_property].desired_state?).to be_truthy
229
+ expect(resource_class.state_properties).to eq [
230
+ resource_class.properties[:custom_property]
231
+ ]
232
+ end
233
+ it "identity_properties does not change custom_property's getter or setter" do
234
+ resource.custom_property = 1
235
+ expect(resource.custom_property).to eq 6
236
+ end
237
+ it "custom_property is returned as the identity" do
238
+ expect(resource.identity).to be_nil
239
+ resource.custom_property = 1
240
+ expect(resource.identity).to eq 6
241
+ end
242
+ end
243
+ end
244
+ end
245
+
246
+ context "Property#identity" do
247
+ with_property ":x, identity: true" do
248
+ it "name is only part of the identity if an identity attribute is defined" do
249
+ expect(resource_class.identity_properties).to eq [ resource_class.properties[:x] ]
250
+ resource.x 'woo'
251
+ expect(resource.identity).to eq 'woo'
252
+ end
253
+ end
254
+
255
+ with_property ":x, identity: true, default: 'xxx'",
256
+ ":y, identity: true, default: 'yyy'",
257
+ ":z, identity: true, default: 'zzz'" do
258
+ it "identity_property raises an error if multiple identity values are defined" do
259
+ expect { resource_class.identity_property }.to raise_error Chef::Exceptions::MultipleIdentityError
260
+ end
261
+ it "identity_attr raises an error if multiple identity values are defined" do
262
+ expect { resource_class.identity_attr }.to raise_error Chef::Exceptions::MultipleIdentityError
263
+ end
264
+ it "identity returns all identity values in a hash if multiple are defined" do
265
+ resource.x 'foo'
266
+ resource.y 'bar'
267
+ resource.z 'baz'
268
+ expect(resource.identity).to eq(x: 'foo', y: 'bar', z: 'baz')
269
+ end
270
+ it "identity returns all values whether any value is set or not" do
271
+ expect(resource.identity).to eq(x: 'xxx', y: 'yyy', z: 'zzz')
272
+ end
273
+ it "identity_properties wipes out any other identity attributes if multiple are defined" do
274
+ resource_class.identity_properties :y
275
+ resource.x 'foo'
276
+ resource.y 'bar'
277
+ resource.z 'baz'
278
+ expect(resource.identity).to eq 'bar'
279
+ end
280
+ end
281
+
282
+ with_property ":x, identity: true, name_property: true" do
283
+ it "identity when x is not defined returns the value of x" do
284
+ expect(resource.identity).to eq 'blah'
285
+ end
286
+ it "state when x is not defined returns the value of x" do
287
+ expect(resource.state_for_resource_reporter).to eq(x: 'blah')
288
+ end
289
+ end
290
+ end
291
+
292
+ # state_properties
293
+ context "Chef::Resource#state_properties" do
294
+ it "state_properties is empty by default" do
295
+ expect(Chef::Resource.state_properties).to eq []
296
+ expect(resource.state_for_resource_reporter).to eq({})
297
+ end
298
+
299
+ with_property ":x", ":y", ":z" do
300
+ it "x, y and z are state attributes" do
301
+ resource.x 1
302
+ resource.y 2
303
+ resource.z 3
304
+ expect(resource_class.state_properties).to eq [
305
+ resource_class.properties[:x],
306
+ resource_class.properties[:y],
307
+ resource_class.properties[:z]
308
+ ]
309
+ expect(resource.state_for_resource_reporter).to eq(x: 1, y: 2, z: 3)
310
+ end
311
+ it "values that are not set are not included in state" do
312
+ resource.x 1
313
+ expect(resource.state_for_resource_reporter).to eq(x: 1)
314
+ end
315
+ it "when no values are set, nothing is included in state" do
316
+ end
317
+ end
318
+
319
+ with_property ":x", ":y, desired_state: false", ":z, desired_state: true" do
320
+ it "x and z are state attributes, and y is not" do
321
+ resource.x 1
322
+ resource.y 2
323
+ resource.z 3
324
+ expect(resource_class.state_properties).to eq [
325
+ resource_class.properties[:x],
326
+ resource_class.properties[:z]
327
+ ]
328
+ expect(resource.state_for_resource_reporter).to eq(x: 1, z: 3)
329
+ end
330
+ end
331
+
332
+ with_property ":x, name_property: true" do
333
+ # it "Unset values with name_property are included in state" do
334
+ # expect(resource.state_for_resource_reporter).to eq({ x: 'blah' })
335
+ # end
336
+ it "Set values with name_property are included in state" do
337
+ resource.x 1
338
+ expect(resource.state_for_resource_reporter).to eq(x: 1)
339
+ end
340
+ end
341
+
342
+ with_property ":x, default: 1" do
343
+ it "Unset values with defaults are not included in state" do
344
+ expect(resource.state_for_resource_reporter).to eq({})
345
+ end
346
+ it "Set values with defaults are included in state" do
347
+ resource.x 1
348
+ expect(resource.state_for_resource_reporter).to eq(x: 1)
349
+ end
350
+ end
351
+
352
+ context "With a class with a normal getter and setter" do
353
+ before do
354
+ resource_class.class_eval do
355
+ def x
356
+ @blah*3
357
+ end
358
+ def x=(value)
359
+ @blah = value*2
360
+ end
361
+ end
362
+ end
363
+ it "state_properties(:x) causes the value to be included in properties" do
364
+ resource_class.state_properties(:x)
365
+ resource.x = 1
366
+
367
+ expect(resource.x).to eq 6
368
+ expect(resource.state_for_resource_reporter).to eq(x: 6)
369
+ end
370
+ end
371
+
372
+ context "When state_properties happens before properties are declared" do
373
+ before do
374
+ resource_class.class_eval do
375
+ state_properties :x
376
+ property :x
377
+ end
378
+ end
379
+ it "the property works and is in state_properties" do
380
+ expect(resource_class.state_properties).to include(resource_class.properties[:x])
381
+ resource.x = 1
382
+ expect(resource.x).to eq 1
383
+ expect(resource.state_for_resource_reporter).to eq(x: 1)
384
+ end
385
+ end
386
+
387
+ with_property ":x, Integer, identity: true" do
388
+ it "state_properties(:x) leaves the property in desired_state" do
389
+ resource_class.state_properties(:x)
390
+ resource.x 10
391
+
392
+ expect(resource_class.properties[:x].desired_state?).to be_truthy
393
+ expect(resource_class.state_properties).to eq [
394
+ resource_class.properties[:x]
395
+ ]
396
+ expect(resource.state_for_resource_reporter).to eq(x: 10)
397
+ end
398
+ it "state_properties(:x) does not turn off validation" do
399
+ resource_class.state_properties(:x)
400
+ expect { resource.x 'ouch' }.to raise_error Chef::Exceptions::ValidationFailed
401
+ end
402
+ it "state_properties(:x) does not turn off identity" do
403
+ resource_class.state_properties(:x)
404
+ resource.x 10
405
+
406
+ expect(resource_class.identity_properties).to eq [ resource_class.properties[:x] ]
407
+ expect(resource_class.properties[:x].identity?).to be_truthy
408
+ expect(resource.identity).to eq 10
409
+ end
410
+ end
411
+
412
+ with_property ":x, Integer, identity: true, desired_state: false" do
413
+ before do
414
+ resource_class.class_eval do
415
+ def y
416
+ 20
417
+ end
418
+ end
419
+ end
420
+
421
+ it "state_properties(:x) leaves x identical" do
422
+ old_value = resource_class.properties[:y]
423
+ resource_class.state_properties(:x)
424
+ resource.x 10
425
+
426
+ expect(resource_class.properties[:y].object_id).to eq old_value.object_id
427
+
428
+ expect(resource_class.properties[:x].desired_state?).to be_truthy
429
+ expect(resource_class.properties[:x].identity?).to be_truthy
430
+ expect(resource_class.identity_properties).to eq [
431
+ resource_class.properties[:x]
432
+ ]
433
+ expect(resource.identity).to eq(10)
434
+ expect(resource_class.state_properties).to eq [
435
+ resource_class.properties[:x]
436
+ ]
437
+ expect(resource.state_for_resource_reporter).to eq(x: 10)
438
+ end
439
+
440
+ it "state_properties(:y) adds y to desired state" do
441
+ old_value = resource_class.properties[:x]
442
+ resource_class.state_properties(:y)
443
+ resource.x 10
444
+
445
+ expect(resource_class.properties[:x].object_id).to eq old_value.object_id
446
+ expect(resource_class.properties[:x].desired_state?).to be_falsey
447
+ expect(resource_class.properties[:y].desired_state?).to be_truthy
448
+ expect(resource_class.state_properties).to eq [
449
+ resource_class.properties[:y]
450
+ ]
451
+ expect(resource.state_for_resource_reporter).to eq(y: 20)
452
+ end
453
+
454
+ context "With a subclassed resource" do
455
+ let(:subresource_class) do
456
+ new_resource_name = self.class.new_resource_name
457
+ Class.new(resource_class) do
458
+ resource_name new_resource_name
459
+ end
460
+ end
461
+ let(:subresource) do
462
+ subresource_class.new('blah')
463
+ end
464
+
465
+ it "state_properties(:x) adds x to desired state" do
466
+ old_value = resource_class.properties[:y]
467
+ subresource_class.state_properties(:x)
468
+ subresource.x 10
469
+
470
+ expect(subresource_class.properties[:y].object_id).to eq old_value.object_id
471
+
472
+ expect(subresource_class.properties[:x].desired_state?).to be_truthy
473
+ expect(subresource_class.properties[:x].identity?).to be_truthy
474
+ expect(subresource_class.identity_properties).to eq [
475
+ subresource_class.properties[:x]
476
+ ]
477
+ expect(subresource.identity).to eq(10)
478
+ expect(subresource_class.state_properties).to eq [
479
+ subresource_class.properties[:x]
480
+ ]
481
+ expect(subresource.state_for_resource_reporter).to eq(x: 10)
482
+ end
483
+
484
+ it "state_properties(:y) adds y to desired state" do
485
+ old_value = resource_class.properties[:x]
486
+ subresource_class.state_properties(:y)
487
+ subresource.x 10
488
+
489
+ expect(subresource_class.properties[:x].object_id).to eq old_value.object_id
490
+ expect(subresource_class.properties[:y].desired_state?).to be_truthy
491
+ expect(subresource_class.state_properties).to eq [
492
+ subresource_class.properties[:y]
493
+ ]
494
+ expect(subresource.state_for_resource_reporter).to eq(y: 20)
495
+
496
+ expect(subresource_class.properties[:x].identity?).to be_truthy
497
+ expect(subresource_class.identity_properties).to eq [
498
+ subresource_class.properties[:x]
499
+ ]
500
+ expect(subresource.identity).to eq(10)
501
+ end
502
+ end
503
+ end
504
+ end
505
+
506
+ end