chefspec 0.6.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,14 +1,17 @@
1
1
  require 'chef'
2
- require 'rspec'
3
2
  require 'chefspec/chef_runner'
4
3
  require 'chefspec/version'
5
- require 'chefspec/matchers/execute'
6
- require 'chefspec/matchers/file'
7
- require 'chefspec/matchers/link'
8
- require 'chefspec/matchers/log'
9
- require 'chefspec/matchers/package'
10
- require 'chefspec/matchers/service'
11
- require 'chefspec/matchers/shared'
12
- require 'chefspec/matchers/file_content'
4
+ if defined?(RSpec)
5
+ require 'chefspec/matchers/cron'
6
+ require 'chefspec/matchers/execute'
7
+ require 'chefspec/matchers/file'
8
+ require 'chefspec/matchers/link'
9
+ require 'chefspec/matchers/log'
10
+ require 'chefspec/matchers/package'
11
+ require 'chefspec/matchers/service'
12
+ require 'chefspec/matchers/shared'
13
+ require 'chefspec/matchers/file_content'
14
+ end
15
+ require 'chefspec/minitest'
13
16
  require 'chefspec/monkey_patches/hash'
14
17
  require 'chefspec/monkey_patches/provider'
@@ -14,6 +14,7 @@ module ChefSpec
14
14
  @resources = []
15
15
 
16
16
  attr_accessor :resources
17
+ attr_reader :run_context
17
18
  attr_reader :node
18
19
 
19
20
  # Instantiate a new runner to run examples with.
@@ -85,11 +86,11 @@ module ChefSpec
85
86
 
86
87
  @resources = []
87
88
  if @client.respond_to?(:setup_run_context) # 0.10.x
88
- run_context = @client.setup_run_context
89
+ @run_context = @client.setup_run_context
89
90
  else
90
- run_context = Chef::RunContext.new(@client.node, Chef::CookbookCollection.new(Chef::CookbookLoader.new)) # 0.9.x
91
+ @run_context = Chef::RunContext.new(@client.node, Chef::CookbookCollection.new(Chef::CookbookLoader.new)) # 0.9.x
91
92
  end
92
- runner = Chef::Runner.new(run_context)
93
+ runner = Chef::Runner.new(@run_context)
93
94
  runner.converge
94
95
  self
95
96
  end
@@ -134,6 +135,14 @@ module ChefSpec
134
135
  find_resource('link', target_file)
135
136
  end
136
137
 
138
+ # Find a crontab entry declared with the given name
139
+ #
140
+ # @param [String] name of a crontab
141
+ # @return [Chef::Resource::Cron] The matching cron resource, or Nil
142
+ def cron(name)
143
+ find_resource('cron', name)
144
+ end
145
+
137
146
  # This runner as a string.
138
147
  #
139
148
  # @return [String] Currently includes the run_list. Format of the string may change between versions of this gem.
@@ -161,7 +170,7 @@ module ChefSpec
161
170
  #
162
171
  # @return [String] The path to the cookbooks directory
163
172
  def default_cookbook_path
164
- Pathname.new(File.join(caller(2).first.split(':').slice(0..-3).first, "..", "..", "..")).cleanpath
173
+ Pathname.new(File.join(caller(2).first.split(':').slice(0..-3).join(':'), '..', '..', '..')).cleanpath
165
174
  end
166
175
 
167
176
  # Find the resource with the declared type and name
@@ -0,0 +1,7 @@
1
+ require 'chefspec/matchers/shared'
2
+
3
+ module ChefSpec
4
+ module Matchers
5
+ define_resource_matchers([:create, :delete], [:cron], :name)
6
+ end
7
+ end
@@ -0,0 +1,199 @@
1
+ module ChefSpec
2
+ # Supports running minitest-chef-handler examples without a real converge.
3
+ #
4
+ # This is experimental and comes with the following caveats:
5
+ #
6
+ # * This support is intended only to run existing examples at present. This
7
+ # means that it has not been tested with examples that explicitly create a
8
+ # new ChefRunner.
9
+ # * Examples that shell out or access the file system using Ruby will fail.
10
+ # This support only ensures that minitest-chef-handler matchers work.
11
+ # * Only a subset of minitest-chef-handler matchers are currently supported
12
+ # , equivalent to the built-in RSpec matchers.
13
+ # * It is assumed that your spec class includes the `MiniTest::Chef` modules
14
+ # directly. Add this include for ChefSpec mode:
15
+ #
16
+ # include ChefSpec::MiniTest
17
+ module MiniTest
18
+
19
+ # minitest-chef-handler declares methods for each resource that use
20
+ # `load_current_resource` to fetch the current state of the node. We override
21
+ # these and instantiate fresh resources.
22
+ def self.override_minitest_resources(run_context, within_module)
23
+ ::Chef::Resource.constants.each do |resource|
24
+ if Chef::Resource.const_get(resource).respond_to?(:ancestors) and
25
+ Chef::Resource.const_get(resource).ancestors.include? Chef::Resource
26
+ method_name = Chef::Mixin::ConvertToClassName.
27
+ convert_to_snake_case(resource.to_s).to_sym
28
+ within_module.send(:define_method, method_name) do |name|
29
+ Chef::Resource.const_get(resource).new(name).tap do |r|
30
+ r.run_context = run_context
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ # minitest-chef-handler patches `Chef::Resource` to handle file uid and gid
38
+ # conversion. In our instance these values are not populated by the
39
+ # provider and will instead be whatever was specified against the resource
40
+ # so we remove this conversion.
41
+ def self.remove_file_id_conversion
42
+ ::Chef::Resource.class_eval do
43
+ def resource_value(attribute)
44
+ send(attribute)
45
+ end
46
+ end
47
+ end
48
+
49
+ def self.override_minitest_assertions(chef_run)
50
+
51
+ remove_file_id_conversion
52
+
53
+ ::MiniTest::Chef::Assertions.class_eval do
54
+
55
+ def assert_includes_content(file, content)
56
+ assert file_includes_content?(file, content),
57
+ "Expected file '#{file.path}' to include the specified content"
58
+ file
59
+ end
60
+
61
+ def refute_includes_content(file, content)
62
+ refute file_includes_content?(file, content),
63
+ "Expected file '#{file.path}' not to include the specified content"
64
+ file
65
+ end
66
+
67
+ def assert_path_exists(file_or_dir)
68
+ paths = file_resources + resources_by_type(:directory)
69
+ actual_resource = paths.find{|file| file_or_dir.path == file.path and
70
+ ! Array(file.action).include?(:delete)}
71
+ assert actual_resource, "Expected path '#{file_or_dir.path}' to exist"
72
+ actual_resource
73
+ end
74
+
75
+ def refute_path_exists(file_or_dir)
76
+ paths = file_resources + resources_by_type(:directory)
77
+ actual_resource = paths.find{|file| file_or_dir.path == file.path and
78
+ Array(file.action).include?(:delete)}
79
+ assert actual_resource, "Expected path '#{file_or_dir.path}' not to exist"
80
+ actual_resource
81
+ end
82
+
83
+ private
84
+
85
+ def same_resource_type?(expected_resource, actual_resource)
86
+ case actual_resource.resource_name.to_s
87
+ when /file$/
88
+ expected_resource.resource_name == :file
89
+ else
90
+ expected_resource.resource_name == actual_resource.resource_name
91
+ end
92
+ end
93
+
94
+ def find_resource(expected_resource, field)
95
+ resources.find do |actual_resource|
96
+ same_resource_type?(expected_resource, actual_resource) and
97
+ expected_resource.send(field) == actual_resource.send(field)
98
+ end
99
+ end
100
+
101
+ def self.override_assertion(assert_name, field, wont_actions = [])
102
+ [:assert, :refute].each do |prefix|
103
+ define_method("#{prefix}_#{assert_name}") do |expected_resource|
104
+ actual_resource = find_resource(expected_resource, field)
105
+ is_wont = false
106
+ if actual_resource
107
+ is_wont = ! (Array(actual_resource.action) & wont_actions).empty?
108
+ end
109
+ # If we are refuting then the assertion is actually positive,
110
+ # we are for example asserting that the package has the `:remove`
111
+ # action.
112
+ if prefix == :assert || is_wont
113
+ assert actual_resource
114
+ else
115
+ refute actual_resource
116
+ end
117
+ actual_resource
118
+ end
119
+ end
120
+ end
121
+
122
+ override_assertion :installed, :package_name, [:remove, :purge]
123
+ override_assertion :enabled, :enabled, [:disable]
124
+ override_assertion :running, :running, [:stop]
125
+
126
+ def file_resources
127
+ resources.select{|r| r.resource_name.to_s.end_with?('file')} +
128
+ resources.select{|r| r.resource_name == :template}
129
+ end
130
+
131
+ def resources_by_type(type)
132
+ resources.select{|r| r.resource_name == type}
133
+ end
134
+
135
+ def cookbook_file_content(file_resource)
136
+ file = cookbook_files(file_resource.cookbook_name).find do |f|
137
+ Pathname.new(f).basename.to_s == file_resource.path
138
+ end
139
+ File.read(file)
140
+ end
141
+
142
+ def cookbook_files(cookbook)
143
+ run_context.cookbook_collection[cookbook].file_filenames
144
+ end
145
+
146
+ def file_includes_content?(file, content)
147
+ file_resources.any? do |f|
148
+ f.path == file.path &&
149
+ case f.resource_name
150
+ when :cookbook_file
151
+ cookbook_file_content(f)
152
+ when :file
153
+ f.content
154
+ when :template
155
+ render(f, node)
156
+ else raise NotImplementedError,
157
+ ":#{f.resource_name} not supported for comparison"
158
+ end.include?(content)
159
+ end
160
+ end
161
+
162
+ end
163
+ end
164
+
165
+ def self.share_object(spec_mod, name, obj)
166
+ spec_mod.send(:define_method, name) do
167
+ obj
168
+ end
169
+ end
170
+
171
+ def self.included(other_mod)
172
+ self.support_fake_converge(other_mod) if spec_module?(other_mod)
173
+ end
174
+
175
+ def self.support_fake_converge(spec_mod)
176
+ chef_run = ChefSpec::ChefRunner.new(
177
+ :cookbook_path => default_cookbook_path).converge recipe_for_module(spec_mod)
178
+ override_minitest_resources(chef_run.run_context, spec_mod)
179
+ override_minitest_assertions(chef_run)
180
+ share_object(spec_mod, :node, chef_run.node)
181
+ share_object(spec_mod, :resources, chef_run.resources)
182
+ share_object(spec_mod, :run_context, chef_run.run_context)
183
+ end
184
+
185
+ def self.recipe_for_module(mod)
186
+ mod.to_s.split('::')[-2..-1].join('::')
187
+ end
188
+
189
+ def self.spec_module?(mod)
190
+ # minitest-chef-handler module names are of the form:
191
+ # `recipe::example::default`
192
+ recipe_for_module(mod) == recipe_for_module(mod).downcase
193
+ end
194
+
195
+ def self.default_cookbook_path
196
+ Pathname.new(File.join(caller(3).first.split(':').slice(0..-3).first, "..", "..", "..")).cleanpath
197
+ end
198
+ end
199
+ end
@@ -1,4 +1,4 @@
1
1
  module ChefSpec
2
2
  # The gem version
3
- VERSION = '0.6.1'
3
+ VERSION = '0.7.0'
4
4
  end
metadata CHANGED
@@ -1,79 +1,89 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: chefspec
3
- version: !ruby/object:Gem::Version
4
- hash: 5
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.7.0
5
5
  prerelease:
6
- segments:
7
- - 0
8
- - 6
9
- - 1
10
- version: 0.6.1
11
6
  platform: ruby
12
- authors:
7
+ authors:
13
8
  - Andrew Crump
14
9
  autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
-
18
- date: 2012-06-21 00:00:00 Z
19
- dependencies:
20
- - !ruby/object:Gem::Dependency
21
- version_requirements: &id001 !ruby/object:Gem::Requirement
12
+ date: 2012-08-27 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: chef
16
+ requirement: !ruby/object:Gem::Requirement
22
17
  none: false
23
- requirements:
24
- - - ">="
25
- - !ruby/object:Gem::Version
26
- hash: 35
27
- segments:
28
- - 0
29
- - 9
30
- - 12
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
31
21
  version: 0.9.12
32
- requirement: *id001
33
22
  type: :runtime
34
23
  prerelease: false
35
- name: chef
36
- - !ruby/object:Gem::Dependency
37
- version_requirements: &id002 !ruby/object:Gem::Requirement
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 0.9.12
30
+ - !ruby/object:Gem::Dependency
31
+ name: erubis
32
+ requirement: !ruby/object:Gem::Requirement
38
33
  none: false
39
- requirements:
40
- - - ">="
41
- - !ruby/object:Gem::Version
42
- hash: 3
43
- segments:
44
- - 0
45
- version: "0"
46
- requirement: *id002
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
47
38
  type: :runtime
48
39
  prerelease: false
49
- name: erubis
50
- - !ruby/object:Gem::Dependency
51
- version_requirements: &id003 !ruby/object:Gem::Requirement
40
+ version_requirements: !ruby/object:Gem::Requirement
52
41
  none: false
53
- requirements:
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: minitest-chef-handler
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
54
51
  - - ~>
55
- - !ruby/object:Gem::Version
56
- hash: 39
57
- segments:
58
- - 2
59
- - 10
60
- - 0
61
- version: 2.10.0
62
- requirement: *id003
52
+ - !ruby/object:Gem::Version
53
+ version: 0.6.0
63
54
  type: :runtime
64
55
  prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 0.6.0
62
+ - !ruby/object:Gem::Dependency
65
63
  name: rspec
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: 2.11.0
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: 2.11.0
66
78
  description: Write RSpec examples for Opscode Chef recipes
67
79
  email:
68
80
  executables: []
69
-
70
81
  extensions: []
71
-
72
82
  extra_rdoc_files: []
73
-
74
- files:
83
+ files:
75
84
  - lib/chef/knife/cookbook_create_specs.rb
76
85
  - lib/chefspec/chef_runner.rb
86
+ - lib/chefspec/matchers/cron.rb
77
87
  - lib/chefspec/matchers/execute.rb
78
88
  - lib/chefspec/matchers/file.rb
79
89
  - lib/chefspec/matchers/file_content.rb
@@ -82,43 +92,41 @@ files:
82
92
  - lib/chefspec/matchers/package.rb
83
93
  - lib/chefspec/matchers/service.rb
84
94
  - lib/chefspec/matchers/shared.rb
95
+ - lib/chefspec/minitest.rb
85
96
  - lib/chefspec/monkey_patches/hash.rb
86
97
  - lib/chefspec/monkey_patches/provider.rb
87
98
  - lib/chefspec/version.rb
88
99
  - lib/chefspec.rb
89
100
  homepage: http://acrmp.github.com/chefspec
90
- licenses:
101
+ licenses:
91
102
  - MIT
92
103
  post_install_message:
93
104
  rdoc_options: []
94
-
95
- require_paths:
105
+ require_paths:
96
106
  - lib
97
- required_ruby_version: !ruby/object:Gem::Requirement
107
+ required_ruby_version: !ruby/object:Gem::Requirement
98
108
  none: false
99
- requirements:
100
- - - ">="
101
- - !ruby/object:Gem::Version
102
- hash: 3
103
- segments:
109
+ requirements:
110
+ - - ! '>='
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ segments:
104
114
  - 0
105
- version: "0"
106
- required_rubygems_version: !ruby/object:Gem::Requirement
115
+ hash: -516424648685498603
116
+ required_rubygems_version: !ruby/object:Gem::Requirement
107
117
  none: false
108
- requirements:
109
- - - ">="
110
- - !ruby/object:Gem::Version
111
- hash: 3
112
- segments:
118
+ requirements:
119
+ - - ! '>='
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ segments:
113
123
  - 0
114
- version: "0"
124
+ hash: -516424648685498603
115
125
  requirements: []
116
-
117
126
  rubyforge_project:
118
- rubygems_version: 1.8.10
127
+ rubygems_version: 1.8.19
119
128
  signing_key:
120
129
  specification_version: 3
121
- summary: chefspec-0.6.1
130
+ summary: chefspec-0.7.0
122
131
  test_files: []
123
-
124
132
  has_rdoc: