rspec-chef 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source :rubygems
2
+
3
+ gemspec
4
+
5
+ group :development, :test do
6
+ gem 'rake', '~> 0.9.2'
7
+ end
8
+
@@ -0,0 +1,76 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ rspec-chef (0.1.0)
5
+ chef
6
+ json
7
+ rspec
8
+
9
+ GEM
10
+ remote: http://rubygems.org/
11
+ specs:
12
+ bunny (0.7.8)
13
+ chef (0.10.8)
14
+ bunny (>= 0.6.0)
15
+ erubis
16
+ highline
17
+ json (>= 1.4.4, <= 1.6.1)
18
+ mixlib-authentication (>= 1.1.0)
19
+ mixlib-cli (>= 1.1.0)
20
+ mixlib-config (>= 1.1.2)
21
+ mixlib-log (>= 1.3.0)
22
+ moneta
23
+ net-ssh (~> 2.1.3)
24
+ net-ssh-multi (~> 1.1.0)
25
+ ohai (>= 0.6.0)
26
+ rest-client (>= 1.0.4, < 1.7.0)
27
+ treetop (~> 1.4.9)
28
+ uuidtools
29
+ diff-lcs (1.1.3)
30
+ erubis (2.7.0)
31
+ highline (1.6.9)
32
+ json (1.6.1)
33
+ mime-types (1.17.2)
34
+ mixlib-authentication (1.1.4)
35
+ mixlib-log
36
+ mixlib-cli (1.2.2)
37
+ mixlib-config (1.1.2)
38
+ mixlib-log (1.3.0)
39
+ moneta (0.6.0)
40
+ net-ssh (2.1.4)
41
+ net-ssh-gateway (1.1.0)
42
+ net-ssh (>= 1.99.1)
43
+ net-ssh-multi (1.1)
44
+ net-ssh (>= 2.1.4)
45
+ net-ssh-gateway (>= 0.99.0)
46
+ ohai (0.6.10)
47
+ mixlib-cli
48
+ mixlib-config
49
+ mixlib-log
50
+ systemu (~> 2.2.0)
51
+ yajl-ruby
52
+ polyglot (0.3.3)
53
+ rake (0.9.2.2)
54
+ rest-client (1.6.7)
55
+ mime-types (>= 1.16)
56
+ rspec (2.7.0)
57
+ rspec-core (~> 2.7.0)
58
+ rspec-expectations (~> 2.7.0)
59
+ rspec-mocks (~> 2.7.0)
60
+ rspec-core (2.7.1)
61
+ rspec-expectations (2.7.0)
62
+ diff-lcs (~> 1.1.2)
63
+ rspec-mocks (2.7.0)
64
+ systemu (2.2.0)
65
+ treetop (1.4.10)
66
+ polyglot
67
+ polyglot (>= 0.3.1)
68
+ uuidtools (2.1.2)
69
+ yajl-ruby (1.1.0)
70
+
71
+ PLATFORMS
72
+ ruby
73
+
74
+ DEPENDENCIES
75
+ rake (~> 0.9.2)
76
+ rspec-chef!
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ == rspec-chef
2
+
3
+ Copyright (c) 2011 David Calavera
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,128 @@
1
+ # RSpec matchers and examples for your Chef recipes
2
+
3
+ Test your Chef cookbooks nicely.
4
+
5
+ This project is heavily inspired by RSpec-puppet.
6
+
7
+ ## Installation
8
+
9
+ ```
10
+ gem install rspec-chef
11
+ ```
12
+
13
+ ## Example groups
14
+
15
+ ### Recipes
16
+
17
+ Follow this structure to organize your recipes:
18
+
19
+ ```
20
+ library
21
+ |
22
+ +-- cookbooks
23
+ |
24
+ +-- specs
25
+ |
26
+ +-- recipes
27
+ |
28
+ +-- <recipe_name>_spec.rb
29
+ ```
30
+
31
+ Or force the example group into the spec:
32
+
33
+ ```ruby
34
+
35
+ describe 'cookbook::recipe', :type => :recipe do
36
+ ...
37
+ end
38
+ ```
39
+
40
+ ## Matchers
41
+
42
+ All of the standard RSpec matchers are available for you to use when testing Chef recipes.
43
+
44
+ *Checking if a recipe contains a resource*
45
+
46
+ Use the matcher `contain_<resource_type>` matcher:
47
+
48
+ ```ruby
49
+ it { should contain_remote_file }
50
+ ```
51
+
52
+ It can take the name of the resource as a parameter:
53
+
54
+ ```ruby
55
+ it { should contain_remote_file('/etc/chef/dna.json') }
56
+ ```
57
+
58
+ It can take the parameters that the resource takes:
59
+
60
+ ```ruby
61
+ it { should contain_remote_file('/etc/chef/dna.json', {:action => :nothing}) }
62
+ ```
63
+
64
+ Use the chained method `with` to test the further attributes that the resource gets:
65
+
66
+ ```ruby
67
+ it { should contain_remote_file('/etc/chef/dna.json').with(:source, 'dna.json.erb') }
68
+ ```
69
+
70
+ Use the chained method `without` to the the resource doesn't get further unexpected attributes:
71
+
72
+ ```ruby
73
+ it { should contain_remote_file('/etc/chef/dna.json').without(:owner, :group) }
74
+ ```
75
+
76
+ ## Settings
77
+
78
+ *Cookbooks path*
79
+
80
+ The path to the cookbooks can be specified using `let` for each group:
81
+
82
+ ```ruby
83
+ describe 'foo::bar' do
84
+ let(:cookbook_path) { File.expand_path('../cookbooks', __FILE__) }
85
+ end
86
+ ```
87
+
88
+ Or it can be set as a global RSpec setting:
89
+
90
+ ```ruby
91
+ RSpec.configure do |c|
92
+ c.cookbook_path File.expand_path('../cookbooks', __FILE__)
93
+ end
94
+ ```
95
+
96
+ *JSON attributes*
97
+
98
+ Each recipe includes a node. This node can be feeded with a hash using `let` in the description group:
99
+
100
+ ```ruby
101
+ describe 'foo::bar' do
102
+ let(:json_attributes) { {:foo => :bar} }
103
+ end
104
+ ```
105
+
106
+ With a file in our file system:
107
+
108
+ ```ruby
109
+ describe 'foo::bar' do
110
+ let(:json_attributes) { '/etc/chef/dna.json' }
111
+ end
112
+ ```
113
+
114
+ Or with plain JSON:
115
+
116
+ ```ruby
117
+ describe 'foo::bar' do
118
+ let(:json_attributes) { '{"foo": "bar"}' }
119
+ end
120
+ ```
121
+
122
+ The JSON attributes can alson be set as a global RSpec setting:
123
+
124
+ ```ruby
125
+ RSpec.configure do |c|
126
+ c.json_attributes {}
127
+ end
128
+ ```
@@ -0,0 +1,129 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'date'
4
+
5
+ #############################################################################
6
+ #
7
+ # Helper functions
8
+ #
9
+ #############################################################################
10
+
11
+ def name
12
+ @name ||= Dir['*.gemspec'].first.split('.').first
13
+ end
14
+
15
+ def version
16
+ line = File.read("lib/#{name}.rb")[/^\s*VERSION\s*=\s*.*/]
17
+ line.match(/.*VERSION\s*=\s*['"](.*)['"]/)[1]
18
+ end
19
+
20
+ def date
21
+ Date.today.to_s
22
+ end
23
+
24
+ def rubyforge_project
25
+ name
26
+ end
27
+
28
+ def gemspec_file
29
+ "#{name}.gemspec"
30
+ end
31
+
32
+ def gem_file
33
+ "#{name}-#{version}.gem"
34
+ end
35
+
36
+ def replace_header(head, header_name)
37
+ head.sub!(/(\.#{header_name}\s*= ').*'/) { "#{$1}#{send(header_name)}'"}
38
+ end
39
+
40
+ #############################################################################
41
+ #
42
+ # Standard tasks
43
+ #
44
+ #############################################################################
45
+
46
+ require 'rspec/core/rake_task'
47
+
48
+ RSpec::Core::RakeTask.new(:rspec) do |spec|
49
+ spec.rspec_opts = ['--color', "--format documentation"]
50
+ end
51
+
52
+ task :default => :rspec
53
+
54
+ desc "Open an irb session preloaded with this library"
55
+ task :console do
56
+ sh "bundle exec irb -rubygems -I ./lib -r #{name}.rb"
57
+ end
58
+
59
+ #############################################################################
60
+ #
61
+ # Packaging tasks
62
+ #
63
+ #############################################################################
64
+
65
+ desc "release a new version of rspec-chef, tag the version and push the gem"
66
+ task :release => :build do
67
+ unless `git branch` =~ /^\* master$/
68
+ puts "You must be on the master branch to release!"
69
+ exit!
70
+ end
71
+ sh "git commit --allow-empty -a -m 'Release #{version}'"
72
+ sh "git tag v#{version}"
73
+ sh "git push origin master"
74
+ sh "git push --tags"
75
+ sh "gem push pkg/#{gem_file}"
76
+ end
77
+
78
+ desc "install rspec-chef gem in this box"
79
+ task :install => :build do
80
+ sh "gem install pkg/#{gem_file}"
81
+ end
82
+
83
+ desc "build rspec-chef gem"
84
+ task :build => 'gemspec' do
85
+ sh "mkdir -p pkg"
86
+ sh "gem build #{gemspec_file}"
87
+ sh "mv #{gem_file} pkg"
88
+ end
89
+
90
+ desc "generate a valid gemspec file"
91
+ task :gemspec => :validate do
92
+ # read spec file and split out manifest section
93
+ spec = File.read(gemspec_file)
94
+ head, manifest, tail = spec.split(" # = MANIFEST =\n")
95
+
96
+ # replace name version and date
97
+ replace_header(head, :name)
98
+ replace_header(head, :version)
99
+ replace_header(head, :date)
100
+ #comment this out if your rubyforge_project has a different name
101
+ replace_header(head, :rubyforge_project)
102
+
103
+ # determine file list from git ls-files
104
+ files = `git ls-files`.
105
+ split("\n").
106
+ sort.
107
+ reject { |file| file =~ /^\./ }.
108
+ reject { |file| file =~ /^(rdoc|pkg|src|racklib)/ }.
109
+ map { |file| " #{file}" }.
110
+ join("\n")
111
+
112
+ # piece file back together and write
113
+ manifest = " s.files = %w[\n#{files}\n ]\n"
114
+ spec = [head, manifest, tail].join(" # = MANIFEST =\n")
115
+ File.open(gemspec_file, 'w') { |io| io.write(spec) }
116
+ puts "Updated #{gemspec_file}"
117
+ end
118
+
119
+ task :validate do
120
+ libfiles = Dir['lib/*'] - ["lib/#{name}.rb", "lib/#{name}"]
121
+ unless libfiles.empty?
122
+ puts "Directory `lib` should only contain a `#{name}.rb` file and `#{name}` dir."
123
+ exit!
124
+ end
125
+ unless Dir['VERSION*'].empty?
126
+ puts "A `VERSION` file at root level violates Gem best practices."
127
+ exit!
128
+ end
129
+ end
@@ -0,0 +1,17 @@
1
+ require 'rspec'
2
+ require 'chef'
3
+ require 'rspec-chef/chef_support'
4
+ require 'rspec-chef/json_support'
5
+ require 'rspec-chef/matchers'
6
+ require 'rspec-chef/examples'
7
+
8
+ module RSpec
9
+ module Chef
10
+ VERSION = '0.1.0'
11
+ end
12
+ end
13
+
14
+ RSpec.configure do |c|
15
+ c.add_setting :cookbook_path, :default => '/etc/chef/recipes'
16
+ c.add_setting :json_attributes, :default => {}
17
+ end
@@ -0,0 +1,18 @@
1
+ module RSpec
2
+ module Chef
3
+ module ChefSupport
4
+ def lookup_recipe(cookbook_name, cookbook_path, dna)
5
+ recipe_name = ::Chef::Recipe.parse_recipe_name(cookbook_name)
6
+
7
+ cookbook_collection = ::Chef::CookbookCollection.new(::Chef::CookbookLoader.new(cookbook_path))
8
+ node = ::Chef::Node.new
9
+ node.consume_attributes(dna)
10
+
11
+ run_context = ::Chef::RunContext.new(node, cookbook_collection)
12
+
13
+ cookbook = run_context.cookbook_collection[recipe_name[0]]
14
+ cookbook.load_recipe(recipe_name[1], run_context)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,11 @@
1
+ require 'rspec-chef/examples/define_recipe_group.rb'
2
+
3
+ RSpec.configure do |c|
4
+ def c.escaped_path(*parts)
5
+ Regexp.compile(parts.join('[\\\/]'))
6
+ end
7
+
8
+ c.include RSpec::Chef::DefineRecipeGroup, :type => :recipe, :example_group => {
9
+ :file_path => c.escaped_path(%w[spec recipes])
10
+ }
11
+ end
@@ -0,0 +1,23 @@
1
+ module RSpec
2
+ module Chef
3
+ module DefineRecipeGroup
4
+ include RSpec::Chef::Matchers
5
+ include JSONSupport
6
+ include ChefSupport
7
+
8
+ def subject
9
+ @recipe ||= recipe
10
+ end
11
+
12
+ def recipe
13
+ ::Chef::Config[:solo] = true
14
+ ::Chef::Config[:cookbook_path] = self.respond_to?(:cookbook_path) ? cookbook_path : RSpec.configuration.cookbook_path
15
+ dna = json(self.respond_to?(:json_attributes) ? json_attributes : RSpec.configuration.json_attributes)
16
+
17
+ cookbook_name = self.class.top_level_description.downcase
18
+
19
+ lookup_recipe(cookbook_name, ::Chef::Config[:cookbook_path], dna)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,17 @@
1
+ module RSpec
2
+ module Chef
3
+ module JSONSupport
4
+ # Transform JSON data into a Hash
5
+ # If data is the path to a file it reads the file and transform its content.
6
+ def json(data)
7
+ if data.is_a?(Hash)
8
+ data
9
+ elsif File.file?(data)
10
+ ::Chef::JSONCompat.from_json(File.read(data)) rescue {}
11
+ else
12
+ ::Chef::JSONCompat.from_json(data) rescue {}
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1 @@
1
+ require 'rspec-chef/matchers/contain_resource'
@@ -0,0 +1,77 @@
1
+ module RSpec
2
+ module Chef
3
+ module Matchers
4
+ CONTAIN_PATTERN = /^contain_(.+)/
5
+
6
+ def method_missing(method, *args, &block)
7
+ return RSpec::Chef::Matchers::ContainResource.new(method, *args, &block) if method.to_s =~ CONTAIN_PATTERN
8
+ super
9
+ end
10
+
11
+ class ContainResource
12
+ def initialize(type, *args, &block)
13
+ @type = type.to_s[CONTAIN_PATTERN, 1]
14
+ @name = args.shift
15
+ @params = args.shift || {}
16
+ @expected_attributes = {}
17
+ @unexpected_attributes = []
18
+ @errors = []
19
+ end
20
+
21
+ def matches?(recipe)
22
+ lookup = @type
23
+ lookup << "[#{@name.to_s}]" if @name
24
+
25
+ resource = recipe.resources(lookup)
26
+ return false unless resource
27
+
28
+ matches = true
29
+ @params.each do |key, value|
30
+ unless (real_value = resource.params[key.to_sym]) == value
31
+ @errors << "#{key} expected to be #{value} but it is #{real_value}"
32
+ matches = false
33
+ end
34
+ end
35
+
36
+ @expected_attributes.each do |key, value|
37
+ unless (real_value = resource.send(key.to_sym)) == value
38
+ @errors << "#{key} expected to be #{value} but it is #{real_value}"
39
+ matches = false
40
+ end
41
+ end
42
+
43
+ @unexpected_attributes.flatten.each do |attr|
44
+ unless (real_value = resource.send(attr.to_sym)) == nil
45
+ @errors << "#{attr} expected to be nil but it is #{real_value}"
46
+ matches = false
47
+ end
48
+ end
49
+
50
+ matches
51
+ end
52
+
53
+ def description
54
+ %Q{include Resource[#{@type} @name="#{@name}"]}
55
+ end
56
+
57
+ def failure_message_for_should
58
+ %Q{expected that the recipe would #{description}#{errors}}
59
+ end
60
+
61
+ def errors
62
+ @errors.empty? ? "" : " with #{@errors.join(', and ')}"
63
+ end
64
+
65
+ def with(attribute, value)
66
+ @expected_attributes[attribute] = value
67
+ self
68
+ end
69
+
70
+ def without(*attributes)
71
+ @unexpected_attributes << attributes
72
+ self
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,73 @@
1
+ ## This is the rakegem gemspec template. Make sure you read and understand
2
+ ## all of the comments. Some sections require modification, and others can
3
+ ## be deleted if you don't need them. Once you understand the contents of
4
+ ## this file, feel free to delete any comments that begin with two hash marks.
5
+ ## You can find comprehensive Gem::Specification documentation, at
6
+ ## http://docs.rubygems.org/read/chapter/20
7
+ Gem::Specification.new do |s|
8
+ s.specification_version = 2 if s.respond_to? :specification_version=
9
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
10
+ s.rubygems_version = '1.3.5'
11
+
12
+ ## Leave these as is they will be modified for you by the rake gemspec task.
13
+ ## If your rubyforge_project name is different, then edit it and comment out
14
+ ## the sub! line in the Rakefile
15
+ s.name = 'rspec-chef'
16
+ s.version = '0.1.0'
17
+ s.date = '2011-12-30'
18
+ s.rubyforge_project = 'rspec-chef'
19
+
20
+ ## Make sure your summary is short. The description may be as long
21
+ ## as you like.
22
+ s.summary = "Rspec matchers and examples for your Chef recipes"
23
+ s.description = s.summary
24
+
25
+ ## List the primary authors. If there are a bunch of authors, it's probably
26
+ ## better to set the email to an email list or something. If you don't have
27
+ ## a custom homepage, consider using your GitHub URL or the like.
28
+ s.authors = ["David Calavera"]
29
+ s.email = 'david.calavera@gmail.com'
30
+ s.homepage = 'http://github.com/calavera/rspec-chef'
31
+
32
+ ## This gets added to the $LOAD_PATH so that 'lib/NAME.rb' can be required as
33
+ ## require 'NAME.rb' or'/lib/NAME/file.rb' can be as require 'NAME/file.rb'
34
+ s.require_paths = %w[lib]
35
+
36
+ ## List your runtime dependencies here. Runtime dependencies are those
37
+ ## that are needed for an end user to actually USE your code.
38
+ s.add_dependency('chef')
39
+ s.add_dependency('rspec')
40
+ s.add_dependency('json')
41
+
42
+ ## Leave this section as-is. It will be automatically generated from the
43
+ ## contents of your Git repository via the gemspec task. DO NOT REMOVE
44
+ ## THE MANIFEST COMMENTS, they are used as delimiters by the task.
45
+ # = MANIFEST =
46
+ s.files = %w[
47
+ Gemfile
48
+ Gemfile.lock
49
+ LICENSE
50
+ README.md
51
+ Rakefile
52
+ lib/rspec-chef.rb
53
+ lib/rspec-chef/chef_support.rb
54
+ lib/rspec-chef/examples.rb
55
+ lib/rspec-chef/examples/define_recipe_group.rb
56
+ lib/rspec-chef/json_support.rb
57
+ lib/rspec-chef/matchers.rb
58
+ lib/rspec-chef/matchers/contain_resource.rb
59
+ rspec-chef.gemspec
60
+ spec/fixtures/cookbooks/foo/recipes/default.rb
61
+ spec/fixtures/cookbooks/foo/recipes/install.rb
62
+ spec/rspec-chef/chef_support_spec.rb
63
+ spec/rspec-chef/examples/define_recipe_group_spec.rb
64
+ spec/rspec-chef/json_support_spec.rb
65
+ spec/rspec-chef/matchers/contain_resource_spec.rb
66
+ spec/spec_helper.rb
67
+ ]
68
+ # = MANIFEST =
69
+
70
+ ## Test files will be grabbed from the file list. Make sure the path glob
71
+ ## matches what you actually use.
72
+ ## s.test_files = s.files.select { |path| path =~ %r{^spec/.*_spec\.rb} }
73
+ end
@@ -0,0 +1 @@
1
+ remote_file '/tmp/foo'
@@ -0,0 +1,5 @@
1
+ remote_file node[:path] do
2
+ source 'base_remote'
3
+ mode 0755
4
+ end
5
+
@@ -0,0 +1,17 @@
1
+ require File.expand_path('../spec_helper', File.dirname(__FILE__))
2
+
3
+ class RSpecChefSupport
4
+ include RSpec::Chef::ChefSupport
5
+ end
6
+
7
+ describe RSpecChefSupport do
8
+ it "returns the default recipe if we only provide the cookbook name" do
9
+ recipe = subject.lookup_recipe('foo', COOKBOOKS, {})
10
+ recipe.recipe_name.should == 'default'
11
+ end
12
+
13
+ it "returns the specific recipe if we provide its name" do
14
+ recipe = subject.lookup_recipe('foo::install', COOKBOOKS, {:path => 'foo'})
15
+ recipe.recipe_name.should == 'install'
16
+ end
17
+ end
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+
3
+ describe "foo", :type => :recipe do
4
+ let(:cookbook_path) { COOKBOOKS }
5
+
6
+ it { subject.should contain_remote_file('/tmp/foo') }
7
+ end
8
+
9
+ describe "foo::install", :type => :recipe do
10
+ let(:cookbook_path) { COOKBOOKS }
11
+ let(:json_attributes) { {:path => '/tmp/foo'} }
12
+
13
+ it { should contain_remote_file('/tmp/foo').with(:source, 'base_remote').with(:mode, 0755) }
14
+ end
@@ -0,0 +1,39 @@
1
+ require File.expand_path('../spec_helper', File.dirname(__FILE__))
2
+
3
+ class ChefJSONSupport
4
+ include RSpec::Chef::JSONSupport
5
+ end
6
+
7
+ describe ChefJSONSupport do
8
+ require 'tempfile'
9
+
10
+ it "returns a hash if the parameter is a hash" do
11
+ subject.json({:foo => :bar}).should == {:foo => :bar}
12
+ end
13
+
14
+ it "returns the content of a dna file if the parameter is a file path" do
15
+ path = Tempfile.open('dna.json') do |file|
16
+ file.write('{"foo": "bar"}')
17
+ file.path
18
+ end
19
+
20
+ subject.json(path).should == {'foo' => 'bar'}
21
+ end
22
+
23
+ it "returns and empty hash if it cannot parse the file" do
24
+ path = Tempfile.open('dna.json') do |file|
25
+ file.write('foo: bar')
26
+ file.path
27
+ end
28
+
29
+ subject.json(path).should == {}
30
+ end
31
+
32
+ it "returns the json parsed when the parameter is a json string" do
33
+ subject.json('{"foo": "bar"}').should == {'foo' => 'bar'}
34
+ end
35
+
36
+ it "returns an empty hash is it cannot parse the json" do
37
+ subject.json('foo: bar').should == {}
38
+ end
39
+ end
@@ -0,0 +1,104 @@
1
+ require File.expand_path('../../spec_helper', File.dirname(__FILE__))
2
+
3
+
4
+ class MockResource
5
+ attr_reader :params
6
+
7
+ def initialize(params = {}, attributes = {})
8
+ @params = params
9
+ @attributes = attributes
10
+ end
11
+
12
+ def method_missing(method, *attrs)
13
+ @attributes[method]
14
+ end
15
+ end
16
+
17
+ class MockRecipe
18
+ def initialize(resources = {})
19
+ @resources = resources
20
+ end
21
+
22
+ def resources(name)
23
+ @resources[name]
24
+ end
25
+ end
26
+
27
+ describe RSpec::Chef::Matchers::ContainResource do
28
+
29
+ context "validating recipes" do
30
+ it "matches a resource with a name" do
31
+ recipe = MockRecipe.new('remote_file[foo]' => MockResource.new)
32
+ matcher = described_class.new(:contain_remote_file, :foo)
33
+
34
+ matcher.matches?(recipe).should be_true
35
+ end
36
+
37
+ it "matches a resource without a name" do
38
+ recipe = MockRecipe.new('remote_file' => MockResource.new)
39
+ matcher = described_class.new(:contain_remote_file)
40
+
41
+ matcher.matches?(recipe).should be_true
42
+ end
43
+
44
+ it "matches a resource with parameters" do
45
+ recipe = MockRecipe.new('remote_file[foo]' => MockResource.new(:bar => :baz))
46
+ matcher = described_class.new(:contain_remote_file, :foo, {:bar => :baz})
47
+
48
+ matcher.matches?(recipe).should be_true
49
+ end
50
+
51
+ it "doesn't match a resource which params don't match" do
52
+ recipe = MockRecipe.new('remote_file[foo]' => MockResource.new(:bar => :baz))
53
+ matcher = described_class.new(:contain_remote_file, :foo, {:bar => :koi})
54
+
55
+ matcher.matches?(recipe).should be_false
56
+ end
57
+
58
+ it "matches a resource with expected attributes" do
59
+ recipe = MockRecipe.new('remote_file[foo]' => MockResource.new({}, {:version => '0.1'}))
60
+ matcher = described_class.new(:contain_remote_file, :foo).with(:version, '0.1')
61
+
62
+ matcher.matches?(recipe).should be_true
63
+ end
64
+
65
+ it "doesn't match a resource which expected attributes don't match" do
66
+ recipe = MockRecipe.new('remote_file[foo]' => MockResource.new({}, {:version => '0.1'}))
67
+ matcher = described_class.new(:contain_remote_file, :foo).with(:version, '1.1')
68
+
69
+ matcher.matches?(recipe).should be_false
70
+ end
71
+
72
+ it "matches a resource without unexpected attributes" do
73
+ recipe = MockRecipe.new('remote_file[foo]' => MockResource.new)
74
+ matcher = described_class.new(:contain_remote_file, :foo).without(:version)
75
+
76
+ matcher.matches?(recipe).should be_true
77
+ end
78
+
79
+ it "mathes a resource without several unexpected attributes" do
80
+ recipe = MockRecipe.new('remote_file[foo]' => MockResource.new)
81
+ matcher = described_class.new(:contain_remote_file, :foo).without(:version, :notif)
82
+
83
+ matcher.matches?(recipe).should be_true
84
+ end
85
+
86
+ it "doesn't match a resource with unexpected attributes" do
87
+ recipe = MockRecipe.new('remote_file[foo]' => MockResource.new({}, {:version => '0.1'}))
88
+ matcher = described_class.new(:contain_remote_file, :foo).without(:version)
89
+
90
+ matcher.matches?(recipe).should be_false
91
+ end
92
+ end
93
+
94
+ it "includes a description" do
95
+ matcher = described_class.new(:contain_remote_file, :foo)
96
+
97
+ matcher.description.should == %q{include Resource[remote_file @name="foo"]}
98
+ end
99
+
100
+ it "includes a message for should failure" do
101
+ matcher = described_class.new(:contain_remote_file, :foo)
102
+ matcher.failure_message_for_should.should == %q{expected that the recipe would include Resource[remote_file @name="foo"]}
103
+ end
104
+ end
@@ -0,0 +1,8 @@
1
+ require 'rspec'
2
+
3
+ $LOAD_PATH << File.expand_path('../lib', File.dirname(__FILE__))
4
+ require 'rspec-chef'
5
+
6
+ COOKBOOKS = File.expand_path('../fixtures/cookbooks', __FILE__)
7
+
8
+ RSpec.configure
metadata ADDED
@@ -0,0 +1,100 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rspec-chef
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - David Calavera
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-12-30 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: chef
16
+ requirement: &2156821580 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *2156821580
25
+ - !ruby/object:Gem::Dependency
26
+ name: rspec
27
+ requirement: &2156821120 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *2156821120
36
+ - !ruby/object:Gem::Dependency
37
+ name: json
38
+ requirement: &2156820700 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *2156820700
47
+ description: Rspec matchers and examples for your Chef recipes
48
+ email: david.calavera@gmail.com
49
+ executables: []
50
+ extensions: []
51
+ extra_rdoc_files: []
52
+ files:
53
+ - Gemfile
54
+ - Gemfile.lock
55
+ - LICENSE
56
+ - README.md
57
+ - Rakefile
58
+ - lib/rspec-chef.rb
59
+ - lib/rspec-chef/chef_support.rb
60
+ - lib/rspec-chef/examples.rb
61
+ - lib/rspec-chef/examples/define_recipe_group.rb
62
+ - lib/rspec-chef/json_support.rb
63
+ - lib/rspec-chef/matchers.rb
64
+ - lib/rspec-chef/matchers/contain_resource.rb
65
+ - rspec-chef.gemspec
66
+ - spec/fixtures/cookbooks/foo/recipes/default.rb
67
+ - spec/fixtures/cookbooks/foo/recipes/install.rb
68
+ - spec/rspec-chef/chef_support_spec.rb
69
+ - spec/rspec-chef/examples/define_recipe_group_spec.rb
70
+ - spec/rspec-chef/json_support_spec.rb
71
+ - spec/rspec-chef/matchers/contain_resource_spec.rb
72
+ - spec/spec_helper.rb
73
+ homepage: http://github.com/calavera/rspec-chef
74
+ licenses: []
75
+ post_install_message:
76
+ rdoc_options: []
77
+ require_paths:
78
+ - lib
79
+ required_ruby_version: !ruby/object:Gem::Requirement
80
+ none: false
81
+ requirements:
82
+ - - ! '>='
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ segments:
86
+ - 0
87
+ hash: -2959191673565686387
88
+ required_rubygems_version: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ requirements: []
95
+ rubyforge_project: rspec-chef
96
+ rubygems_version: 1.8.10
97
+ signing_key:
98
+ specification_version: 2
99
+ summary: Rspec matchers and examples for your Chef recipes
100
+ test_files: []