chefspec 7.3.3 → 7.3.4
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.
- checksums.yaml +4 -4
- data/Gemfile +23 -0
- data/Rakefile +77 -0
- data/chefspec.gemspec +29 -0
- data/lib/chefspec/version.rb +1 -1
- data/spec/spec_helper.rb +13 -0
- data/spec/support/hash.rb +35 -0
- data/spec/unit/cacher_spec.rb +70 -0
- data/spec/unit/coverage/filters_spec.rb +60 -0
- data/spec/unit/deprecations_spec.rb +53 -0
- data/spec/unit/errors_spec.rb +57 -0
- data/spec/unit/expect_exception_spec.rb +32 -0
- data/spec/unit/macros_spec.rb +119 -0
- data/spec/unit/matchers/do_nothing_matcher.rb +5 -0
- data/spec/unit/matchers/include_recipe_matcher_spec.rb +38 -0
- data/spec/unit/matchers/link_to_matcher_spec.rb +55 -0
- data/spec/unit/matchers/notifications_matcher_spec.rb +40 -0
- data/spec/unit/matchers/render_file_matcher_spec.rb +68 -0
- data/spec/unit/matchers/resource_matcher_spec.rb +5 -0
- data/spec/unit/matchers/state_attrs_matcher_spec.rb +68 -0
- data/spec/unit/matchers/subscribes_matcher_spec.rb +65 -0
- data/spec/unit/renderer_spec.rb +69 -0
- data/spec/unit/server_runner_spec.rb +28 -0
- data/spec/unit/solo_runner_spec.rb +171 -0
- data/spec/unit/stubs/command_registry_spec.rb +27 -0
- data/spec/unit/stubs/command_stub_spec.rb +61 -0
- data/spec/unit/stubs/data_bag_item_registry_spec.rb +39 -0
- data/spec/unit/stubs/data_bag_item_stub_spec.rb +36 -0
- data/spec/unit/stubs/data_bag_registry_spec.rb +39 -0
- data/spec/unit/stubs/data_bag_stub_spec.rb +35 -0
- data/spec/unit/stubs/registry_spec.rb +29 -0
- data/spec/unit/stubs/search_registry_spec.rb +39 -0
- data/spec/unit/stubs/search_stub_spec.rb +36 -0
- data/spec/unit/stubs/stub_spec.rb +64 -0
- metadata +34 -2
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ChefSpec::ExpectException do
|
4
|
+
context 'when there have been no `raise_error` matchers' do
|
5
|
+
subject { described_class.new(Exception) }
|
6
|
+
|
7
|
+
it 'does not match' do
|
8
|
+
allow(RSpec::Matchers::BuiltIn::RaiseError).to receive(:last_run).and_return(nil)
|
9
|
+
expect(subject.expected?).to be_falsy
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'when the last error does not match the expected type' do
|
14
|
+
subject { described_class.new(RuntimeError) }
|
15
|
+
|
16
|
+
it 'does not match' do
|
17
|
+
last_error = double('last error', last_error_for_chefspec: ArgumentError)
|
18
|
+
allow(RSpec::Matchers::BuiltIn::RaiseError).to receive(:last_run).and_return(last_error)
|
19
|
+
expect(subject.expected?).to be_falsy
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'when the last error matches the expected type' do
|
24
|
+
subject { described_class.new(RuntimeError) }
|
25
|
+
|
26
|
+
it 'does not match' do
|
27
|
+
last_error = double('last error', last_error_for_chefspec: RuntimeError)
|
28
|
+
allow(RSpec::Matchers::BuiltIn::RaiseError).to receive(:last_run).and_return(last_error)
|
29
|
+
expect(subject.expected?).to be_truthy
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ChefSpec::API::Stubs do
|
4
|
+
describe '#stub_command' do
|
5
|
+
let(:command_stub) { double('command') }
|
6
|
+
|
7
|
+
it 'adds the command to the command registry' do
|
8
|
+
allow(ChefSpec::Stubs::CommandStub).to receive(:new).and_return(command_stub)
|
9
|
+
stub_command('echo "hello"')
|
10
|
+
|
11
|
+
expect(ChefSpec::Stubs::CommandRegistry.stubs).to include(command_stub)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '#stub_search' do
|
16
|
+
let(:search_stub) { double('search') }
|
17
|
+
|
18
|
+
it 'adds the query to the search registry' do
|
19
|
+
allow(ChefSpec::Stubs::SearchStub).to receive(:new).and_return(search_stub)
|
20
|
+
stub_search(:node, '*:*')
|
21
|
+
|
22
|
+
expect(ChefSpec::Stubs::SearchRegistry.stubs).to include(search_stub)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '#stub_data_bag' do
|
27
|
+
let(:data_bag_stub) { double('data_bag') }
|
28
|
+
|
29
|
+
it 'adds the query to the data_bag registry' do
|
30
|
+
allow(ChefSpec::Stubs::DataBagStub).to receive(:new).and_return(data_bag_stub)
|
31
|
+
stub_data_bag(:users)
|
32
|
+
|
33
|
+
expect(ChefSpec::Stubs::DataBagRegistry.stubs).to include(data_bag_stub)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe '#stub_data_bag_item' do
|
38
|
+
let(:data_bag_item_stub) { double('data_bag_item') }
|
39
|
+
|
40
|
+
it 'adds the query to the data_bag_item registry' do
|
41
|
+
allow(ChefSpec::Stubs::DataBagItemStub).to receive(:new).and_return(data_bag_item_stub)
|
42
|
+
stub_data_bag_item(:users, 'id')
|
43
|
+
|
44
|
+
expect(ChefSpec::Stubs::DataBagItemRegistry.stubs).to include(data_bag_item_stub)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe '#stub_node' do
|
49
|
+
it 'returns a Chef::Node' do
|
50
|
+
expect(stub_node).to be_a(Chef::Node)
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'defaults the node name to `node.example`' do
|
54
|
+
node = stub_node
|
55
|
+
expect(node.name).to eq('node.example')
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'sets the node name when given' do
|
59
|
+
node = stub_node('example.com')
|
60
|
+
expect(node.name).to eq('example.com')
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'sets the automatic attributes' do
|
64
|
+
node = stub_node
|
65
|
+
expect(node.automatic).to eq(Fauxhai.mock.data)
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'sets the automatic attributes with ohai overrides' do
|
69
|
+
node = stub_node('node.example', ohai: { ipaddress: '1.2.3.4' })
|
70
|
+
expect(node['ipaddress']).to eq('1.2.3.4')
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'sets the automatic attributes for a specific platform and version' do
|
74
|
+
node = stub_node('node.example', platform: 'ubuntu', version: '18.04')
|
75
|
+
expect(node.automatic).to eq(Fauxhai.mock(platform: 'ubuntu', version: '18.04').data)
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'sets the automatic attributes from a JSON data path' do
|
79
|
+
allow(File).to receive(:exist?).with('/path/to/json').and_return(true)
|
80
|
+
allow(File).to receive(:read).with('/path/to/json').and_return('{ "ipaddress": "1.2.3.4" }')
|
81
|
+
node = stub_node('node.example', path: '/path/to/json')
|
82
|
+
expect(node['ipaddress']).to eq('1.2.3.4')
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'yields a block' do
|
86
|
+
expect { |block| stub_node(&block) }.to yield_with_args(Chef::Node)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe 'nginx::source' do
|
92
|
+
describe '#described_cookbook' do
|
93
|
+
describe 'nginx::source' do
|
94
|
+
it 'returns the name of the cookbook' do
|
95
|
+
expect(described_cookbook).to eq('nginx')
|
96
|
+
end
|
97
|
+
|
98
|
+
context 'in a nested context' do
|
99
|
+
it 'still returns the name of the cookbook' do
|
100
|
+
expect(described_cookbook).to eq('nginx')
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
describe '#described_recipe' do
|
107
|
+
describe 'nginx::source' do
|
108
|
+
it 'returns the cookbook::recipe' do
|
109
|
+
expect(described_recipe).to eq('nginx::source')
|
110
|
+
end
|
111
|
+
|
112
|
+
context 'in a nested context' do
|
113
|
+
it 'still retrns the cookbook::recipe' do
|
114
|
+
expect(described_recipe).to eq('nginx::source')
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ChefSpec::Matchers::IncludeRecipeMatcher do
|
4
|
+
let(:chef_run) { double('chef run', run_context: { loaded_recipes: %w(one two three) }) }
|
5
|
+
subject { described_class.new('one::default') }
|
6
|
+
|
7
|
+
describe '#failure_message' do
|
8
|
+
it 'has the right value' do
|
9
|
+
subject.matches?(chef_run)
|
10
|
+
expect(subject.failure_message)
|
11
|
+
.to eq(%q(expected ["one::default", "two::default", "three::default"] to include "one::default"))
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '#failure_message_when_negated' do
|
16
|
+
it 'has the right value' do
|
17
|
+
subject.matches?(chef_run)
|
18
|
+
expect(subject.failure_message_when_negated)
|
19
|
+
.to eq(%q(expected "one::default" to not be included))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '#description' do
|
24
|
+
it 'has the right value' do
|
25
|
+
subject.matches?(chef_run)
|
26
|
+
expect(subject.description).to eq(%q(include recipe "one::default"))
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'matches when the recipe is included' do
|
31
|
+
expect(subject.matches?(chef_run)).to be_truthy
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'does not match when the recipe is not included' do
|
35
|
+
failure = described_class.new('nope')
|
36
|
+
expect(failure.matches?(chef_run)).to be_falsy
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ChefSpec::Matchers::LinkToMatcher do
|
4
|
+
let(:from) { '/var/www' }
|
5
|
+
let(:to) { '/var/html' }
|
6
|
+
let(:link) do
|
7
|
+
Chef::Resource::Link.new(from).tap do |link|
|
8
|
+
link.to(to)
|
9
|
+
link.perform_action(:create)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
subject { described_class.new(to) }
|
13
|
+
|
14
|
+
describe '#failure_message' do
|
15
|
+
it 'has the right value' do
|
16
|
+
subject.matches?(link)
|
17
|
+
expect(subject.failure_message)
|
18
|
+
.to eq(%Q(expected "link[#{from}]" to link to "#{to}" but was "#{to}"))
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#failure_message_when_negated' do
|
23
|
+
it 'has the right value' do
|
24
|
+
subject.matches?(link)
|
25
|
+
expect(subject.failure_message_when_negated)
|
26
|
+
.to eq(%Q(expected "link[#{from}]" to not link to "#{to}"))
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '#description' do
|
31
|
+
it 'has the right value' do
|
32
|
+
subject.matches?(link)
|
33
|
+
expect(subject.description).to eq(%Q(link to "#{to}"))
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'when the link is correct' do
|
38
|
+
it 'matches' do
|
39
|
+
expect(subject.matches?(link)).to be_truthy
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'adds the link to the coverage report' do
|
43
|
+
expect(ChefSpec::Coverage).to receive(:cover!).with(link)
|
44
|
+
subject.matches?(link)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context 'when the link is not correct' do
|
49
|
+
subject { described_class.new('/nope/bad/path/bro') }
|
50
|
+
|
51
|
+
it 'does not match' do
|
52
|
+
expect(subject.matches?(link)).to be_falsy
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ChefSpec::Matchers::NotificationsMatcher do
|
4
|
+
subject { described_class.new('execute[install]') }
|
5
|
+
let(:package) do
|
6
|
+
double('package',
|
7
|
+
name: 'package',
|
8
|
+
to_s: 'package[foo]',
|
9
|
+
is_a?: true,
|
10
|
+
performed_action?: true,
|
11
|
+
immediate_notifications: [],
|
12
|
+
delayed_notifications: [],
|
13
|
+
before_notifications: []
|
14
|
+
)
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#failure_message' do
|
18
|
+
it 'has the right value' do
|
19
|
+
subject.matches?(package)
|
20
|
+
expect(subject.failure_message)
|
21
|
+
.to include %|expected "package[foo]" to notify "execute[install]", but did not.|
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe '#failure_message_when_negated' do
|
26
|
+
it 'has the right value' do
|
27
|
+
subject.matches?(package)
|
28
|
+
expect(subject.failure_message_when_negated)
|
29
|
+
.to eq %|expected "package[foo]" to not notify "execute[install]", but it did.|
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '#description' do
|
34
|
+
it 'has the right value' do
|
35
|
+
subject.matches?(package)
|
36
|
+
expect(subject.description)
|
37
|
+
.to eq %|notify "execute[install]"|
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ChefSpec::Matchers::RenderFileMatcher do
|
4
|
+
let(:path) { '/tmp/thing' }
|
5
|
+
let(:file) { double('file', to: path, to_s: "file[#{path}]", performed_action?: true) }
|
6
|
+
let(:chef_run) { double('chef run', find_resource: file) }
|
7
|
+
subject { described_class.new(path) }
|
8
|
+
|
9
|
+
describe '#with_content' do
|
10
|
+
it 'accepts do/end syntax' do
|
11
|
+
subject.matches?(chef_run)
|
12
|
+
expect(
|
13
|
+
subject.with_content do |content|
|
14
|
+
'Does not raise ArgumentError'
|
15
|
+
end.expected_content.first.call
|
16
|
+
).to eq('Does not raise ArgumentError')
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '#failure_message' do
|
21
|
+
it 'has the right value' do
|
22
|
+
subject.matches?(chef_run)
|
23
|
+
expect(subject.failure_message)
|
24
|
+
.to eq(%Q(expected Chef run to render "#{path}"))
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '#failure_message_when_negated' do
|
29
|
+
it 'has the right value' do
|
30
|
+
subject.matches?(chef_run)
|
31
|
+
expect(subject.failure_message_when_negated)
|
32
|
+
.to eq(%Q(expected file "#{path}" to not be in Chef run))
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '#description' do
|
37
|
+
it 'has the right value' do
|
38
|
+
subject.matches?(chef_run)
|
39
|
+
expect(subject.description).to eq(%Q(render file "#{path}"))
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'has the right value when with_content is chained' do
|
43
|
+
subject.matches?(chef_run)
|
44
|
+
expect(
|
45
|
+
subject.with_content('foo').with_content('bar').description
|
46
|
+
).to eq(%Q(render file "#{path}" with content "foo" with content "bar"))
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context 'when the file is correct' do
|
51
|
+
it 'matches' do
|
52
|
+
expect(subject.matches?(chef_run)).to be_truthy
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'adds the resource to the coverage report' do
|
56
|
+
expect(ChefSpec::Coverage).to receive(:cover!).with(file)
|
57
|
+
subject.matches?(chef_run)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context 'when the file is not correct' do
|
62
|
+
it 'does not match' do
|
63
|
+
allow(chef_run).to receive(:find_resource).and_return(nil)
|
64
|
+
failure = described_class.new('nope')
|
65
|
+
expect(failure.matches?(chef_run)).to be_falsy
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ChefSpec::Matchers::StateAttrsMatcher do
|
4
|
+
subject { described_class.new([:a, :b]) }
|
5
|
+
|
6
|
+
context 'when the resource does not exist' do
|
7
|
+
let(:resource) { nil }
|
8
|
+
before { subject.matches?(resource) }
|
9
|
+
|
10
|
+
it 'does not match' do
|
11
|
+
expect(subject).to_not be_matches(resource)
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'has the correct description' do
|
15
|
+
expect(subject.description).to eq('have state attributes [:a, :b]')
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'has the correct failure message for should' do
|
19
|
+
expect(subject.failure_message).to include <<-EOH.gsub(/^ {8}/, '')
|
20
|
+
expected _something_ to have state attributes, but the _something_ you gave me was nil!
|
21
|
+
Ensure the resource exists before making assertions:
|
22
|
+
|
23
|
+
expect(resource).to be
|
24
|
+
EOH
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'has the correct failure message for should not' do
|
28
|
+
expect(subject.failure_message_when_negated).to include <<-EOH.gsub(/^ {8}/, '')
|
29
|
+
expected _something_ to not have state attributes, but the _something_ you gave me was nil!
|
30
|
+
Ensure the resource exists before making assertions:
|
31
|
+
|
32
|
+
expect(resource).to be
|
33
|
+
EOH
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'when the resource exists' do
|
38
|
+
let(:klass) { double('class', state_attrs: [:a, :b]) }
|
39
|
+
let(:resource) { double('resource', class: klass) }
|
40
|
+
before { subject.matches?(resource) }
|
41
|
+
|
42
|
+
it 'has the correct description' do
|
43
|
+
expect(subject.description).to eq('have state attributes [:a, :b]')
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'has the correct failure message for should' do
|
47
|
+
expect(subject.failure_message).to eq('expected [:a, :b] to equal [:a, :b]')
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'has the correct failure message for should not' do
|
51
|
+
expect(subject.failure_message_when_negated).to eq('expected [:a, :b] to not equal [:a, :b]')
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'matches when the state attributes are correct' do
|
55
|
+
expect(subject).to be_matches(resource)
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'does not match when the state attributes are incorrect' do
|
59
|
+
allow(klass).to receive(:state_attrs).and_return([:c, :d])
|
60
|
+
expect(subject).to_not be_matches(resource)
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'does not match when partial state attribute are incorrect' do
|
64
|
+
allow(klass).to receive(:state_attrs).and_return([:b, :c])
|
65
|
+
expect(subject).to_not be_matches(resource)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ChefSpec::Matchers::SubscribesMatcher do
|
4
|
+
subject { described_class.new('execute[install]') }
|
5
|
+
let(:runner) { double('runner', find_resource: nil) }
|
6
|
+
let(:run_context) { double('run_context', node: node) }
|
7
|
+
let(:node) { double('node', runner: runner) }
|
8
|
+
let(:package) do
|
9
|
+
double('package',
|
10
|
+
name: 'package',
|
11
|
+
to_s: 'package[foo]',
|
12
|
+
run_context: run_context,
|
13
|
+
)
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'when no resource is found' do
|
17
|
+
describe '#failure_message' do
|
18
|
+
it 'has the right value' do
|
19
|
+
subject.matches?(package)
|
20
|
+
expect(subject.failure_message)
|
21
|
+
.to include %|expected _something_ to notify "package[foo]", but the _something_ you gave me was nil! If you are running a test like:|
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'when the resource exists' do
|
27
|
+
let(:execute) do
|
28
|
+
double('execute',
|
29
|
+
name: 'execute',
|
30
|
+
to_s: 'execute[install]',
|
31
|
+
immediate_notifications: [],
|
32
|
+
delayed_notifications: [],
|
33
|
+
before_notifications: []
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
before do
|
38
|
+
allow(runner).to receive(:find_resource).and_return(execute)
|
39
|
+
end
|
40
|
+
|
41
|
+
describe '#failure_message' do
|
42
|
+
it 'has the right value' do
|
43
|
+
subject.matches?(package)
|
44
|
+
expect(subject.failure_message)
|
45
|
+
.to include %|expected "execute[install]" to notify "package[foo]", but did not.|
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe '#failure_message_when_negated' do
|
50
|
+
it 'has the right value' do
|
51
|
+
subject.matches?(package)
|
52
|
+
expect(subject.failure_message_when_negated)
|
53
|
+
.to eq %|expected "execute[install]" to not notify "package[foo]", but it did.|
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe '#description' do
|
59
|
+
it 'has the right value' do
|
60
|
+
subject.matches?(package)
|
61
|
+
expect(subject.description)
|
62
|
+
.to eq %|notify "package[foo]"|
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|