chefspec 3.4.0 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +2 -19
  3. data/CHANGELOG.md +32 -1
  4. data/CONTRIBUTING.md +1 -1
  5. data/README.md +37 -9
  6. data/chefspec.gemspec +2 -3
  7. data/examples/render_file/spec/default_spec.rb +32 -17
  8. data/examples/server/recipes/render_with_cached.rb +3 -0
  9. data/examples/server/spec/client_spec.rb +1 -1
  10. data/examples/server/spec/data_bag_spec.rb +1 -1
  11. data/examples/server/spec/environment_spec.rb +1 -1
  12. data/examples/server/spec/node_spec.rb +1 -1
  13. data/examples/server/spec/render_with_cached_spec.rb +16 -0
  14. data/examples/server/spec/role_spec.rb +1 -1
  15. data/features/server.feature +10 -12
  16. data/features/support/executor.rb +2 -0
  17. data/gemfiles/{chef-11.0.0.gemfile → chef-11.12.0.gemfile} +1 -1
  18. data/gemfiles/chef-master.gemfile +1 -1
  19. data/lib/chefspec.rb +1 -0
  20. data/lib/chefspec/api/erl_call.rb +5 -5
  21. data/lib/chefspec/api/execute.rb +5 -5
  22. data/lib/chefspec/api/powershell_script.rb +5 -5
  23. data/lib/chefspec/api/ruby_block.rb +2 -2
  24. data/lib/chefspec/api/script.rb +13 -2
  25. data/lib/chefspec/cacher.rb +2 -1
  26. data/lib/chefspec/coverage.rb +10 -2
  27. data/lib/chefspec/expect_exception.rb +0 -2
  28. data/lib/chefspec/extensions/chef/lwrp_base.rb +5 -1
  29. data/lib/chefspec/extensions/chef/resource/freebsd_package.rb +19 -0
  30. data/lib/chefspec/librarian.rb +5 -2
  31. data/lib/chefspec/macros.rb +3 -3
  32. data/lib/chefspec/matchers/do_nothing_matcher.rb +4 -2
  33. data/lib/chefspec/matchers/include_recipe_matcher.rb +2 -2
  34. data/lib/chefspec/matchers/link_to_matcher.rb +11 -5
  35. data/lib/chefspec/matchers/notifications_matcher.rb +2 -2
  36. data/lib/chefspec/matchers/render_file_matcher.rb +9 -3
  37. data/lib/chefspec/matchers/resource_matcher.rb +2 -2
  38. data/lib/chefspec/matchers/state_attrs_matcher.rb +2 -2
  39. data/lib/chefspec/matchers/subscribes_matcher.rb +4 -4
  40. data/lib/chefspec/renderer.rb +1 -1
  41. data/lib/chefspec/rspec.rb +1 -0
  42. data/lib/chefspec/runner.rb +2 -1
  43. data/lib/chefspec/server.rb +90 -19
  44. data/lib/chefspec/version.rb +1 -1
  45. data/spec/unit/cacher_spec.rb +1 -1
  46. data/spec/unit/expect_exception_spec.rb +6 -6
  47. data/spec/unit/extensions/lwrp_base_spec.rb +9 -1
  48. data/spec/unit/macros_spec.rb +6 -6
  49. data/spec/unit/matchers/include_recipe_matcher_spec.rb +6 -6
  50. data/spec/unit/matchers/link_to_matcher_spec.rb +31 -15
  51. data/spec/unit/matchers/notifications_matcher_spec.rb +4 -4
  52. data/spec/unit/matchers/render_file_matcher_spec.rb +19 -10
  53. data/spec/unit/matchers/state_attrs_matcher_spec.rb +28 -24
  54. data/spec/unit/matchers/subscribes_matcher_spec.rb +7 -7
  55. data/spec/unit/renderer_spec.rb +7 -7
  56. data/spec/unit/runner_spec.rb +39 -9
  57. metadata +10 -25
  58. data/gemfiles/chef-11.2.0.gemfile +0 -5
  59. data/gemfiles/chef-11.4.4.gemfile +0 -5
  60. data/gemfiles/chef-11.6.0.gemfile +0 -5
  61. data/gemfiles/chef-11.8.0.gemfile +0 -5
@@ -15,19 +15,19 @@ module ChefSpec::API
15
15
  # The Examples section demonstrates the different ways to test a
16
16
  # +powershell_script+ resource with ChefSpec.
17
17
  #
18
- # @example Assert that a +powershell_script+ was runed
18
+ # @example Assert that a +powershell_script+ was run
19
19
  # expect(chef_run).to run_powershell_script('/tmp')
20
20
  #
21
- # @example Assert that a +powershell_script+ was runed with predicate matchers
21
+ # @example Assert that a +powershell_script+ was run with predicate matchers
22
22
  # expect(chef_run).to run_powershell_script('/tmp').with_user('svargo')
23
23
  #
24
- # @example Assert that a +powershell_script+ was runed with attributes
24
+ # @example Assert that a +powershell_script+ was run with attributes
25
25
  # expect(chef_run).to run_powershell_script('/tmp').with(user: 'svargo')
26
26
  #
27
- # @example Assert that a +powershell_script+ was runed using a regex
27
+ # @example Assert that a +powershell_script+ was run using a regex
28
28
  # expect(chef_run).to run_powershell_script('/tmp').with(user: /sva(.+)/)
29
29
  #
30
- # @example Assert that a +powershell_script+ was _not_ runed
30
+ # @example Assert that a +powershell_script+ was _not_ run
31
31
  # expect(chef_run).to_not run_powershell_script('/tmp')
32
32
  #
33
33
  #
@@ -18,10 +18,10 @@ module ChefSpec::API
18
18
  # The Examples section demonstrates the different ways to test a
19
19
  # +ruby_block+ resource with ChefSpec.
20
20
  #
21
- # @example Assert that a +ruby_block+ was runed
21
+ # @example Assert that a +ruby_block+ was run
22
22
  # expect(chef_run).to run_ruby_block('do_something')
23
23
  #
24
- # @example Assert that a +ruby_block+ was _not_ runed
24
+ # @example Assert that a +ruby_block+ was _not_ run
25
25
  # expect(chef_run).to_not run_ruby_block('do_something')
26
26
  #
27
27
  #
@@ -1,8 +1,6 @@
1
1
  module ChefSpec::API
2
2
  # @since 1.0.0
3
3
  module ScriptMatchers
4
- ChefSpec::Runner.define_runner_method :script
5
-
6
4
  #
7
5
  # Assert that a +bash+ resource exists in the Chef run with the
8
6
  # action +:run+. Given a Chef Recipe that runs "command" using
@@ -40,6 +38,8 @@ module ChefSpec::API
40
38
  ChefSpec::Matchers::ResourceMatcher.new(:bash, :run, resource_name)
41
39
  end
42
40
 
41
+ ChefSpec::Runner.define_runner_method :bash
42
+
43
43
  #
44
44
  # Assert that a +csh+ resource exists in the Chef run with the
45
45
  # action +:run+. Given a Chef Recipe that runs "command" using
@@ -77,6 +77,8 @@ module ChefSpec::API
77
77
  ChefSpec::Matchers::ResourceMatcher.new(:csh, :run, resource_name)
78
78
  end
79
79
 
80
+ ChefSpec::Runner.define_runner_method :csh
81
+
80
82
  #
81
83
  # Assert that a +perl+ resource exists in the Chef run with the
82
84
  # action +:run+. Given a Chef Recipe that runs "command" using
@@ -114,6 +116,8 @@ module ChefSpec::API
114
116
  ChefSpec::Matchers::ResourceMatcher.new(:perl, :run, resource_name)
115
117
  end
116
118
 
119
+ ChefSpec::Runner.define_runner_method :perl
120
+
117
121
  #
118
122
  # Assert that a +python+ resource exists in the Chef run with the
119
123
  # action +:run+. Given a Chef Recipe that runs "command" using
@@ -151,6 +155,8 @@ module ChefSpec::API
151
155
  ChefSpec::Matchers::ResourceMatcher.new(:python, :run, resource_name)
152
156
  end
153
157
 
158
+ ChefSpec::Runner.define_runner_method :python
159
+
154
160
  #
155
161
  # Assert that a +ruby+ resource exists in the Chef run with the
156
162
  # action +:run+. Given a Chef Recipe that runs "command" using
@@ -188,6 +194,8 @@ module ChefSpec::API
188
194
  ChefSpec::Matchers::ResourceMatcher.new(:ruby, :run, resource_name)
189
195
  end
190
196
 
197
+ ChefSpec::Runner.define_runner_method :ruby
198
+
191
199
  #
192
200
  # Assert that a +script+ resource exists in the Chef run with the
193
201
  # action +:run+. Given a Chef Recipe that runs "command" using
@@ -224,5 +232,8 @@ module ChefSpec::API
224
232
  def run_script(resource_name)
225
233
  ChefSpec::Matchers::ResourceMatcher.new(:script, :run, resource_name)
226
234
  end
235
+
236
+
237
+ ChefSpec::Runner.define_runner_method :script
227
238
  end
228
239
  end
@@ -34,7 +34,8 @@ module ChefSpec
34
34
  FINALIZER = lambda { |id| @@cache.delete(id) }
35
35
 
36
36
  def cached(name, &block)
37
- location = ancestors.first.metadata[:example_group][:location]
37
+ location = ancestors.first.metadata[:location]
38
+ location ||= ancestors.first.metadata[:parent_example_group][:location]
38
39
 
39
40
  define_method(name) do
40
41
  key = [location, name.to_s].join('.')
@@ -161,11 +161,19 @@ module ChefSpec
161
161
  end
162
162
 
163
163
  def source_file
164
- @source_file ||= shortname(@resource.source_line.split(':').first)
164
+ @source_file ||= if @resource.source_line
165
+ shortname(@resource.source_line.split(':').first)
166
+ else
167
+ 'Unknown'
168
+ end
165
169
  end
166
170
 
167
171
  def source_line
168
- @source_line ||= @resource.source_line.split(':', 2).last.to_i
172
+ @source_line ||= if @resource.source_line
173
+ @resource.source_line.split(':', 2).last.to_i
174
+ else
175
+ 'Unknown'
176
+ end
169
177
  end
170
178
 
171
179
  def touch!
@@ -1,5 +1,3 @@
1
- require 'rspec/matchers/built_in/raise_error'
2
-
3
1
  class RSpec::Matchers::BuiltIn::RaiseError
4
2
  class << self
5
3
  attr_accessor :last_run
@@ -39,7 +39,11 @@ class Chef
39
39
  [self, superclass].each do |resource_holder|
40
40
  look_in_parents = false
41
41
  if resource_holder.const_defined?(class_name, look_in_parents)
42
- resource_holder.send(:remove_const, class_name)
42
+ old_class = resource_holder.send(:remove_const, class_name)
43
+
44
+ if resource_holder.respond_to?(:resource_classes)
45
+ resource_holder.resource_classes.delete(old_class)
46
+ end
43
47
  end
44
48
  end
45
49
  end
@@ -0,0 +1,19 @@
1
+ require 'chef/resource/freebsd_package'
2
+
3
+ class Chef
4
+ class Resource
5
+ class FreebsdPackage < Chef::Resource::Package
6
+ #
7
+ # Chef decided it was a good idea to just shellout inside of a resource.
8
+ # Not only is that a horrible fucking idea, but I got flak when I asked
9
+ # to change it. So we are just going to monkey patch the fucking thing so
10
+ # it does not shell out.
11
+ #
12
+ # @return [false]
13
+ #
14
+ def supports_pkgng?
15
+ false
16
+ end
17
+ end
18
+ end
19
+ end
@@ -25,7 +25,7 @@ module ChefSpec
25
25
  #
26
26
  def setup!
27
27
  env = ::Librarian::Chef::Environment.new(project_path: Dir.pwd)
28
- env.config_db.local['path'] = @tmpdir
28
+ @originalpath, env.config_db.local['path'] = env.config_db.local['path'], @tmpdir
29
29
  ::Librarian::Action::Resolve.new(env).run
30
30
  ::Librarian::Action::Install.new(env).run
31
31
 
@@ -33,9 +33,12 @@ module ChefSpec
33
33
  end
34
34
 
35
35
  #
36
- # Remove the temporary directory.
36
+ # Remove the temporary directory and restore the librarian-chef cookbook path.
37
37
  #
38
38
  def teardown!
39
+ env = ::Librarian::Chef::Environment.new(project_path: Dir.pwd)
40
+ env.config_db.local['path'] = @originalpath
41
+
39
42
  FileUtils.rm_rf(@tmpdir) if File.exists?(@tmpdir)
40
43
  end
41
44
  end
@@ -44,11 +44,11 @@ module ChefSpec
44
44
  scope = self.is_a?(Class) ? self : self.class
45
45
 
46
46
  metahash = scope.metadata
47
- while metahash.has_key?(:example_group)
48
- metahash = metahash[:example_group]
47
+ while metahash.has_key?(:parent_example_group)
48
+ metahash = metahash[:parent_example_group]
49
49
  end
50
50
 
51
- metahash[:description_args].first.to_s
51
+ metahash[:description].to_s
52
52
  end
53
53
 
54
54
  #
@@ -4,6 +4,8 @@ module ChefSpec::Matchers
4
4
  @resource = resource
5
5
 
6
6
  if @resource
7
+ ChefSpec::Coverage.cover!(@resource)
8
+
7
9
  actions = @resource.performed_actions
8
10
  actions.empty? || actions == [:nothing]
9
11
  else
@@ -15,7 +17,7 @@ module ChefSpec::Matchers
15
17
  'do nothing'
16
18
  end
17
19
 
18
- def failure_message_for_should
20
+ def failure_message
19
21
  if @resource
20
22
  message = %|expected #{@resource} to do nothing, but the following |
21
23
  message << %|actions were performed:|
@@ -35,7 +37,7 @@ module ChefSpec::Matchers
35
37
  end
36
38
  end
37
39
 
38
- def failure_message_for_should_not
40
+ def failure_message_when_negated
39
41
  if @resource
40
42
  message = %|expected #{@resource} to do something, but no actions |
41
43
  message << %|were performed.|
@@ -13,11 +13,11 @@ module ChefSpec::Matchers
13
13
  %Q{include recipe "#{@recipe_name}"}
14
14
  end
15
15
 
16
- def failure_message_for_should
16
+ def failure_message
17
17
  %Q{expected #{loaded_recipes.inspect} to include "#{@recipe_name}"}
18
18
  end
19
19
 
20
- def failure_message_for_should_not
20
+ def failure_message_when_negated
21
21
  %Q{expected "#{@recipe_name}" to not be included}
22
22
  end
23
23
 
@@ -7,16 +7,22 @@ module ChefSpec::Matchers
7
7
  def matches?(link)
8
8
  @link = link
9
9
 
10
- @link.is_a?(Chef::Resource::Link) &&
11
- @link.performed_action?(:create) &&
12
- @path === @link.to
10
+ if @link
11
+ ChefSpec::Coverage.cover!(@link)
12
+
13
+ @link.is_a?(Chef::Resource::Link) &&
14
+ @link.performed_action?(:create) &&
15
+ @path === @link.to
16
+ else
17
+ false
18
+ end
13
19
  end
14
20
 
15
21
  def description
16
22
  %Q{link to "#{@path}"}
17
23
  end
18
24
 
19
- def failure_message_for_should
25
+ def failure_message
20
26
  if @link.nil?
21
27
  %Q{expected "link[#{@path}]" with action :create to be in Chef run}
22
28
  else
@@ -24,7 +30,7 @@ module ChefSpec::Matchers
24
30
  end
25
31
  end
26
32
 
27
- def failure_message_for_should_not
33
+ def failure_message_when_negated
28
34
  %Q{expected "#{@link}" to not link to "#{@path}"}
29
35
  end
30
36
  end
@@ -51,7 +51,7 @@ module ChefSpec::Matchers
51
51
  message
52
52
  end
53
53
 
54
- def failure_message_for_should
54
+ def failure_message
55
55
  if @resource
56
56
  message = %Q{expected "#{@resource}" to notify "#{@expected_resource_type}[#{@expected_resource_name}]"}
57
57
  message << " with action :#{@action}" if @action
@@ -77,7 +77,7 @@ module ChefSpec::Matchers
77
77
  end
78
78
  end
79
79
 
80
- def failure_message_for_should_not
80
+ def failure_message_when_negated
81
81
  if @resource
82
82
  message = %Q{expected "#{@resource}" to not notify "#{@expected_resource_type}[#{@expected_resource_name}]"}
83
83
  message << ", but it did."
@@ -6,7 +6,13 @@ module ChefSpec::Matchers
6
6
 
7
7
  def matches?(runner)
8
8
  @runner = runner
9
- resource && has_create_action? && matches_content?
9
+
10
+ if resource
11
+ ChefSpec::Coverage.cover!(resource)
12
+ has_create_action? && matches_content?
13
+ else
14
+ false
15
+ end
10
16
  end
11
17
 
12
18
  def with_content(expected_content)
@@ -26,7 +32,7 @@ module ChefSpec::Matchers
26
32
  message
27
33
  end
28
34
 
29
- def failure_message_for_should
35
+ def failure_message
30
36
  message = %Q{expected Chef run to render "#{@path}"}
31
37
  if @expected_content
32
38
  message << " matching:"
@@ -41,7 +47,7 @@ module ChefSpec::Matchers
41
47
  message
42
48
  end
43
49
 
44
- def failure_message_for_should_not
50
+ def failure_message_when_negated
45
51
  message = %Q{expected file "#{@path}"}
46
52
  if @expected_content
47
53
  message << " matching:"
@@ -50,7 +50,7 @@ module ChefSpec::Matchers
50
50
  end
51
51
  end
52
52
 
53
- def failure_message_for_should
53
+ def failure_message
54
54
  if resource
55
55
  if resource.performed_action?(@expected_action)
56
56
  if unmatched_parameters.empty?
@@ -79,7 +79,7 @@ module ChefSpec::Matchers
79
79
  end
80
80
  end
81
81
 
82
- def failure_message_for_should_not
82
+ def failure_message_when_negated
83
83
  if resource
84
84
  message = %Q{expected "#{resource.to_s}" actions #{resource.performed_actions.inspect} to not exist}
85
85
  else
@@ -18,7 +18,7 @@ module ChefSpec::Matchers
18
18
  %Q{have state attributes #{@expected_attrs.inspect}}
19
19
  end
20
20
 
21
- def failure_message_for_should
21
+ def failure_message
22
22
  if @resource
23
23
  "expected #{state_attrs.inspect} to equal #{@expected_attrs.inspect}"
24
24
  else
@@ -32,7 +32,7 @@ module ChefSpec::Matchers
32
32
  end
33
33
  end
34
34
 
35
- def failure_message_for_should_not
35
+ def failure_message_when_negated
36
36
  if @resource
37
37
  "expected #{state_attrs.inspect} to not equal " \
38
38
  "#{@expected_attrs.inspect}"
@@ -52,12 +52,12 @@ module ChefSpec::Matchers
52
52
  @instance.description
53
53
  end
54
54
 
55
- def failure_message_for_should
56
- @instance.failure_message_for_should
55
+ def failure_message
56
+ @instance.failure_message
57
57
  end
58
58
 
59
- def failure_message_for_should_not
60
- @instance.failure_message_for_should_not
59
+ def failure_message_when_negated
60
+ @instance.failure_message_when_negated
61
61
  end
62
62
  end
63
63
  end
@@ -108,7 +108,7 @@ module ChefSpec
108
108
  def content_from_cookbook_file(chef_run, cookbook_file)
109
109
  cookbook_name = cookbook_file.cookbook || cookbook_file.cookbook_name
110
110
  cookbook = cookbook_collection(chef_run.node)[cookbook_name]
111
- File.read(cookbook.preferred_filename_on_disk_location(chef_run.node, :files, cookbook_file.source, cookbook_file.path))
111
+ File.read(cookbook.preferred_filename_on_disk_location(chef_run.node, :files, cookbook_file.source))
112
112
  end
113
113
 
114
114
  # The cookbook collection for the current Chef run context. Handles
@@ -10,6 +10,7 @@ RSpec.configure do |config|
10
10
  end
11
11
 
12
12
  config.add_setting :cookbook_path
13
+ config.add_setting :organization, default: 'chefspec'
13
14
  config.add_setting :role_path
14
15
  config.add_setting :log_level, default: :warn
15
16
  config.add_setting :path
@@ -80,6 +80,7 @@ module ChefSpec
80
80
  Chef::Config[:cache_type] = 'Memory'
81
81
  Chef::Config[:client_key] = nil
82
82
  Chef::Config[:cookbook_path] = Array(options[:cookbook_path])
83
+ Chef::Config[:no_lazy_load] = true
83
84
  Chef::Config[:role_path] = Array(options[:role_path])
84
85
  Chef::Config[:force_logger] = true
85
86
  Chef::Config[:solo] = true
@@ -270,7 +271,7 @@ module ChefSpec
270
271
  calling_spec = kaller.find { |line| line =~ /\/spec/ }
271
272
  raise Error::CookbookPathNotFound if calling_spec.nil?
272
273
 
273
- bits = calling_spec.split(':', 2).first.split(File::SEPARATOR)
274
+ bits = calling_spec.split(/:[0-9]/, 2).first.split(File::SEPARATOR)
274
275
  spec_dir = bits.index('spec') || 0
275
276
 
276
277
  File.expand_path(File.join(bits.slice(0, spec_dir), '..'))
@@ -61,6 +61,13 @@ module ChefSpec
61
61
  instance.send(m, *args, &block)
62
62
  end
63
63
 
64
+ #
65
+ # RSpec 3 checks +respond_to?+ for some odd reason.
66
+ #
67
+ def self.respond_to_missing?(m, include_private = false)
68
+ instance.respond_to?(m, include_private) || super
69
+ end
70
+
64
71
  #
65
72
  # @macro entity
66
73
  # @method create_$1(name, data = {})
@@ -105,11 +112,11 @@ module ChefSpec
105
112
  # Convert it to JSON
106
113
  data = JSON.fast_generate(data)
107
114
 
108
- @server.load_data('#{key}' => { name => data })
115
+ load_data(name, '#{key}', data)
109
116
  end
110
117
 
111
118
  def #{method}(name)
112
- data = @server.data_store.get(['#{key}', name])
119
+ data = get('#{key}', name)
113
120
  json = JSON.parse(data)
114
121
 
115
122
  if #{klass}.respond_to?(:json_create)
@@ -122,37 +129,45 @@ module ChefSpec
122
129
  end
123
130
 
124
131
  def #{key}
125
- @server.data_store.list(['#{key}'])
132
+ get('#{key}')
126
133
  end
127
134
 
128
135
  def has_#{method}?(name)
129
- !@server.data_store.get(['#{key}', name]).nil?
136
+ !get('#{key}', name).nil?
130
137
  rescue ChefZero::DataStore::DataNotFoundError
131
138
  false
132
139
  end
133
140
  EOH
134
141
  end
135
142
 
143
+ entity :client, Chef::Client, 'clients'
144
+ entity :data_bag, Chef::DataBag, 'data'
145
+ entity :environment, Chef::Environment, 'environments'
146
+ entity :node, Chef::Node, 'nodes'
147
+ entity :role, Chef::Role, 'roles'
148
+
136
149
  include Singleton
137
150
 
138
151
  attr_reader :server
139
152
 
140
153
  #
141
154
  # Create a new instance of the +ChefSpec::Server+ singleton. This method
142
- # also starts the Chef Zero server in the background.
155
+ # also starts the Chef Zero server in the background under the organization
156
+ # "chefspec".
143
157
  #
144
158
  def initialize
145
159
  @server = ChefZero::Server.new(
160
+ # Set the log level from RSpec, defaulting to warn
146
161
  log_level: RSpec.configuration.log_level || :warn,
162
+
163
+ # Set a random port so ChefSpec may be run in multiple jobs
164
+ port: port,
165
+
166
+ # Disable the "old" way - this is actually +multi_tenant: true+
167
+ single_org: false,
147
168
  )
148
169
  end
149
170
 
150
- entity :client, Chef::Client, 'clients'
151
- entity :data_bag, Chef::DataBag, 'data'
152
- entity :environment, Chef::Environment, 'environments'
153
- entity :node, Chef::Node, 'nodes'
154
- entity :role, Chef::Role, 'roles'
155
-
156
171
  #
157
172
  # Create a new data_bag on the Chef Server. This overrides the method
158
173
  # created by {entity}
@@ -163,7 +178,7 @@ module ChefSpec
163
178
  # the data to load into the data bag
164
179
  #
165
180
  def create_data_bag(name, data = {})
166
- @server.load_data('data' => { name => data })
181
+ load_data(name, 'data', data)
167
182
  end
168
183
 
169
184
  #
@@ -197,7 +212,7 @@ module ChefSpec
197
212
  data = JSON.fast_generate(data)
198
213
  end
199
214
 
200
- @server.load_data('nodes' => { name => data })
215
+ load_data(name, 'nodes', data)
201
216
  end
202
217
 
203
218
  #
@@ -217,6 +232,15 @@ module ChefSpec
217
232
  end
218
233
  end
219
234
 
235
+ #
236
+ # The URL where ChefZero is listening (including the organization).
237
+ #
238
+ # @return [String]
239
+ #
240
+ def chef_server_url
241
+ @chef_server_url ||= File.join(@server.url, 'organizations', organization)
242
+ end
243
+
220
244
  #
221
245
  # Start the Chef Zero server in the background, updating the +Chef::Config+
222
246
  # with the proper +chef_server_url+.
@@ -224,7 +248,7 @@ module ChefSpec
224
248
  def start!
225
249
  unless @server.running?
226
250
  @server.start_background
227
- Chef::Config[:chef_server_url] = @server.url
251
+ Chef::Config[:chef_server_url] = chef_server_url
228
252
  end
229
253
  end
230
254
 
@@ -233,6 +257,7 @@ module ChefSpec
233
257
  #
234
258
  def reset!
235
259
  @server.clear_data
260
+ @server.data_store.create_dir(['organizations'], organization)
236
261
  end
237
262
 
238
263
  #
@@ -246,6 +271,15 @@ module ChefSpec
246
271
 
247
272
  private
248
273
 
274
+ #
275
+ # The name of the Chef organization.
276
+ #
277
+ # @return [String]
278
+ #
279
+ def organization
280
+ RSpec.configuration.organization
281
+ end
282
+
249
283
  #
250
284
  # The directory where any cache information (such as private keys) should
251
285
  # be stored. This cache is destroyed at the end of the run.
@@ -260,16 +294,52 @@ module ChefSpec
260
294
  #
261
295
  # Shortcut method for loading data into Chef Zero.
262
296
  #
263
- # @param [String, Symbol] key
264
- # the key to load
265
297
  # @param [String] name
266
298
  # the name or id of the item to load
299
+ # @param [String, Symbol] key
300
+ # the key to load
267
301
  # @param [Hash] data
268
302
  # the data for the object, which will be converted to JSON and uploaded
269
303
  # to the server
270
304
  #
271
- def load_data(key, name, data = {})
272
- @server.load_data(key.to_s => { name => JSON.fast_generate(data) })
305
+ def load_data(name, key, data = {})
306
+ @server.load_data({ key => { name => data } }, organization)
307
+ end
308
+
309
+ #
310
+ # Get the path to an item in the data store.
311
+ #
312
+ def get(*args)
313
+ if args.size == 1
314
+ @server.data_store.list(with_organization(*args))
315
+ else
316
+ @server.data_store.get(with_organization(*args))
317
+ end
318
+ end
319
+
320
+ #
321
+ # Prefix the given args with the organization. This is used by the data
322
+ # store to fetch elements.
323
+ #
324
+ # @return [Array<String>]
325
+ #
326
+ def with_organization(*args)
327
+ ['organizations', organization, *args]
328
+ end
329
+
330
+ #
331
+ # A randomly assigned, open port for run the Chef Zero server.
332
+ #
333
+ # @return [Fixnum]
334
+ #
335
+ def port
336
+ return @port if @port
337
+
338
+ @server = TCPServer.new('127.0.0.1', 0)
339
+ @port = @server.addr[1].to_i
340
+ @server.close
341
+
342
+ return @port
273
343
  end
274
344
  end
275
345
  end
@@ -277,7 +347,8 @@ end
277
347
  ChefSpec::Server.start!
278
348
 
279
349
  RSpec.configure do |config|
280
- config.after(:each) { ChefSpec::Server.reset! }
350
+ config.before(:each) { ChefSpec::Server.reset! }
351
+ config.after(:each) { ChefSpec::Server.reset! }
281
352
  end
282
353
 
283
354
  at_exit { ChefSpec::Server.stop! }