chef-apply 0.1.17 → 0.1.18

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +0 -7
  3. data/Gemfile.lock +176 -84
  4. data/chef-apply.gemspec +2 -2
  5. data/lib/chef_apply/version.rb +1 -1
  6. data/spec/fixtures/custom_config.toml +2 -0
  7. data/spec/integration/chef-run_spec.rb +41 -0
  8. data/spec/integration/fixtures/chef_help.out +69 -0
  9. data/spec/integration/fixtures/chef_version.out +1 -0
  10. data/spec/integration/spec_helper.rb +55 -0
  11. data/spec/spec_helper.rb +114 -0
  12. data/spec/support/matchers/output_to_terminal.rb +36 -0
  13. data/spec/unit/action/base_spec.rb +89 -0
  14. data/spec/unit/action/converge_target_spec.rb +292 -0
  15. data/spec/unit/action/generate_local_policy_spec.rb +114 -0
  16. data/spec/unit/action/generate_temp_cookbook_spec.rb +75 -0
  17. data/spec/unit/action/install_chef/base_spec.rb +234 -0
  18. data/spec/unit/action/install_chef_spec.rb +69 -0
  19. data/spec/unit/cli/options_spec.rb +75 -0
  20. data/spec/unit/cli/validation_spec.rb +78 -0
  21. data/spec/unit/cli_spec.rb +440 -0
  22. data/spec/unit/config_spec.rb +70 -0
  23. data/spec/unit/errors/ccr_failure_mapper_spec.rb +103 -0
  24. data/spec/unit/file_fetcher_spec.rb +40 -0
  25. data/spec/unit/fixtures/multi-error.out +2 -0
  26. data/spec/unit/log_spec.rb +37 -0
  27. data/spec/unit/recipe_lookup_spec.rb +122 -0
  28. data/spec/unit/startup_spec.rb +283 -0
  29. data/spec/unit/target_host_spec.rb +231 -0
  30. data/spec/unit/target_resolver_spec.rb +380 -0
  31. data/spec/unit/telemeter/sender_spec.rb +140 -0
  32. data/spec/unit/telemeter_spec.rb +191 -0
  33. data/spec/unit/temp_cookbook_spec.rb +199 -0
  34. data/spec/unit/ui/error_printer_spec.rb +173 -0
  35. data/spec/unit/ui/terminal_spec.rb +109 -0
  36. data/spec/unit/version_spec.rb +31 -0
  37. data/warning.txt +3 -0
  38. metadata +34 -2
@@ -0,0 +1,75 @@
1
+ #
2
+ # Copyright:: Copyright (c) 2018 Chef Software Inc.
3
+ # License:: Apache License, Version 2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ require "spec_helper"
19
+ require "mixlib/cli"
20
+ require "chef_apply/cli/options"
21
+ require "chef-config/config"
22
+
23
+ ChefApply::Config.load
24
+
25
+ module ChefApply
26
+ module CLIOptions
27
+ class TestClass
28
+ include Mixlib::CLI
29
+ include ChefApply::CLI::Options
30
+ end
31
+
32
+ def parse(argv)
33
+ parse_options(argv)
34
+ end
35
+ end
36
+ end
37
+
38
+ RSpec.describe ChefApply::CLIOptions do
39
+ let(:klass) { ChefApply::CLIOptions::TestClass.new }
40
+
41
+ it "contains the specified options" do
42
+ expect(klass.options.keys).to eq([
43
+ :version,
44
+ :help,
45
+ :config_path,
46
+ :identity_file,
47
+ :ssl,
48
+ :ssl_verify,
49
+ :protocol,
50
+ :user,
51
+ :password,
52
+ :cookbook_repo_paths,
53
+ :install,
54
+ :sudo,
55
+ :sudo_command,
56
+ :sudo_password,
57
+ :sudo_options
58
+ ])
59
+ end
60
+
61
+ it "persists certain CLI options back to the ChefApply::Config" do
62
+ # First we check the default value beforehand
63
+ expect(ChefApply::Config.connection.winrm.ssl).to eq(false)
64
+ expect(ChefApply::Config.connection.winrm.ssl_verify).to eq(true)
65
+ expect(ChefApply::Config.connection.default_protocol).to eq("ssh")
66
+ expect(ChefApply::Config.chef.cookbook_repo_paths).to_not be_empty
67
+ # Then we set the values and check they are changed
68
+ klass.parse_options(["--ssl", "--no-ssl-verify", "--protocol", "winrm", "--cookbook-repo-paths", "a,b"])
69
+ expect(ChefApply::Config.connection.winrm.ssl).to eq(true)
70
+ expect(ChefApply::Config.connection.winrm.ssl_verify).to eq(false)
71
+ expect(ChefApply::Config.connection.default_protocol).to eq("winrm")
72
+ expect(ChefApply::Config.chef.cookbook_repo_paths).to eq(%w{a b})
73
+ end
74
+
75
+ end
@@ -0,0 +1,78 @@
1
+ require "spec_helper"
2
+ require "chef_apply/error"
3
+ require "chef_apply/cli/validation"
4
+
5
+ RSpec.describe ChefApply::CLI::Validation do
6
+ class Validator
7
+ include ChefApply::CLI::Validation
8
+ end
9
+ subject { Validator.new }
10
+
11
+ context "#validate_params" do
12
+ OptionValidationError = ChefApply::CLI::OptionValidationError
13
+ it "raises an error if not enough params are specified" do
14
+ params = [
15
+ [],
16
+ %w{one}
17
+ ]
18
+ params.each do |p|
19
+ expect { subject.validate_params(p) }.to raise_error(OptionValidationError) do |e|
20
+ e.id == "CHEFVAL002"
21
+ end
22
+ end
23
+ end
24
+
25
+ it "succeeds if the second command is a valid file path" do
26
+ params = %w{target /some/path}
27
+ expect(File).to receive(:exist?).with("/some/path").and_return true
28
+ expect { subject.validate_params(params) }.to_not raise_error
29
+ end
30
+
31
+ it "succeeds if the second argument looks like a cookbook name" do
32
+ params = [
33
+ %w{target cb},
34
+ %w{target cb::recipe}
35
+ ]
36
+ params.each do |p|
37
+ expect { subject.validate_params(p) }.to_not raise_error
38
+ end
39
+ end
40
+
41
+ it "raises an error if the second argument is neither a valid path or a valid cookbook name" do
42
+ params = %w{target weird%name}
43
+ expect { subject.validate_params(params) }.to raise_error(OptionValidationError) do |e|
44
+ e.id == "CHEFVAL004"
45
+ end
46
+ end
47
+
48
+ it "raises an error if properties are not specified as key value pairs" do
49
+ params = [
50
+ %w{one two three four},
51
+ %w{one two three four=value five six=value},
52
+ %w{one two three non.word=value},
53
+ ]
54
+ params.each do |p|
55
+ expect { subject.validate_params(p) }.to raise_error(OptionValidationError) do |e|
56
+ e.id == "CHEFVAL003"
57
+ end
58
+ end
59
+ end
60
+ end
61
+ describe "#properties_from_string" do
62
+ it "parses properties into a hash" do
63
+ provided = %w{key1=value key2=1 key3=true key4=FaLsE key5=0777 key6=https://some.website key7=num1and2digit key_8=underscore}
64
+ expected = {
65
+ "key1" => "value",
66
+ "key2" => 1,
67
+ "key3" => true,
68
+ "key4" => false,
69
+ "key5" => "0777",
70
+ "key6" => "https://some.website",
71
+ "key7" => "num1and2digit",
72
+ "key_8" => "underscore"
73
+ }
74
+ expect(subject.properties_from_string(provided)).to eq(expected)
75
+ end
76
+ end
77
+
78
+ end
@@ -0,0 +1,440 @@
1
+ #
2
+ # Copyright:: Copyright (c) 2018 Chef Software Inc.
3
+ # License:: Apache License, Version 2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ require "spec_helper"
19
+ require "chef_apply/cli"
20
+ require "chef_apply/error"
21
+ require "chef_apply/telemeter"
22
+ require "chef_apply/telemeter/sender"
23
+ require "chef_apply/ui/terminal"
24
+ require "chef_apply/action/generate_temp_cookbook"
25
+
26
+ require "chef-dk/ui"
27
+ require "chef-dk/policyfile_services/export_repo"
28
+ require "chef-dk/policyfile_services/install"
29
+
30
+ RSpec.describe ChefApply::CLI do
31
+ subject { ChefApply::CLI.new(argv) }
32
+ let(:argv) { [] }
33
+ # TODO why isn't this mocked?
34
+ let(:telemetry) { ChefApply::Telemeter.instance }
35
+
36
+ before do
37
+ # Avoid messy object dumps in failures because subject is an object instance
38
+ allow(subject).to receive(:inspect).and_return("The subject instance")
39
+ end
40
+
41
+ describe "run" do
42
+ before do
43
+ # Catch all of the calls by default, to prevent the various
44
+ # startup actions from actually occuring on the workstatoin.
45
+ allow(telemetry).to receive(:timed_run_capture).and_yield
46
+ allow(subject).to receive(:perform_run)
47
+ allow(telemetry).to receive(:commit)
48
+ end
49
+
50
+ it "captures and commits the run to telemetry" do
51
+ expect(telemetry).to receive(:timed_run_capture)
52
+ expect(telemetry).to receive(:commit)
53
+ expect { subject.run }.to exit_with_code(0)
54
+ end
55
+
56
+ it "calls perform_run" do
57
+ expect(subject).to receive(:perform_run)
58
+ expect { subject.run }.to exit_with_code(0)
59
+ end
60
+
61
+ context "perform_run raises WrappedError" do
62
+ let(:e) { ChefApply::WrappedError.new(RuntimeError.new("Test"), "host") }
63
+
64
+ it "prints the error and exits" do
65
+ expect(subject).to receive(:perform_run).and_raise(e)
66
+ expect(ChefApply::UI::ErrorPrinter).to receive(:show_error).with(e)
67
+ expect { subject.run }.to exit_with_code(1)
68
+ end
69
+ end
70
+
71
+ context "perform_run raises SystemExit" do
72
+ it "exits with same exit code" do
73
+ expect(subject).to receive(:perform_run).and_raise(SystemExit.new(99))
74
+ expect { subject.run }.to exit_with_code(99)
75
+ end
76
+ end
77
+
78
+ context "perform_run raises any other exception" do
79
+ let(:e) { Exception.new("test") }
80
+
81
+ it "exits with code 64" do
82
+ expect(subject).to receive(:perform_run).and_raise(e)
83
+ expect(ChefApply::UI::ErrorPrinter).to receive(:dump_unexpected_error).with(e)
84
+ expect { subject.run }.to exit_with_code(64)
85
+ end
86
+ end
87
+ end
88
+
89
+ describe "#perform_run" do
90
+ it "parses options" do
91
+ expect(subject).to receive(:parse_options).with(argv)
92
+ subject.perform_run
93
+ end
94
+
95
+ context "when any error is raised" do
96
+ let(:e) { RuntimeError.new("Test") }
97
+ before do
98
+ allow(subject).to receive(:parse_options).and_raise(e)
99
+ end
100
+
101
+ it "calls handle_perform_error" do
102
+ expect(subject).to receive(:handle_perform_error).with(e)
103
+ subject.perform_run
104
+ end
105
+ end
106
+
107
+ context "when argv is empty" do
108
+ let(:argv) { [] }
109
+ it "shows the help text" do
110
+ expect(subject).to receive(:show_help)
111
+ subject.perform_run
112
+ end
113
+ end
114
+
115
+ context "when help flags are passed" do
116
+ %w{-h --help}.each do |flag|
117
+ context flag do
118
+ let(:argv) { [flag] }
119
+ it "shows the help text" do
120
+ expect(subject).to receive(:show_help)
121
+ subject.perform_run
122
+ end
123
+ end
124
+ end
125
+
126
+ %w{-v --version}.each do |flag|
127
+ context flag do
128
+ let(:argv) { [flag] }
129
+ it "shows the help text" do
130
+ expect(subject).to receive(:show_version)
131
+ subject.perform_run
132
+ end
133
+ end
134
+ end
135
+ end
136
+
137
+ context "when arguments are provided" do
138
+ let(:argv) { ["hostname", "resourcetype", "resourcename", "someproperty=true"] }
139
+ let(:target_hosts) { [double("TargetHost")] }
140
+ before do
141
+ # parse_options sets `cli_argument` - because we stub out parse_options,
142
+ # later calls that rely on cli_arguments will fail without this.
143
+ allow(subject).to receive(:cli_arguments).and_return argv
144
+ end
145
+
146
+ context "and they are valid" do
147
+ it "creates the cookbook locally and converges it" do
148
+ expect(subject).to receive(:parse_options)
149
+ expect(subject).to receive(:validate_params)
150
+ expect(subject).to receive(:resolve_targets).and_return target_hosts
151
+ expect(subject).to receive(:render_cookbook_setup)
152
+ expect(subject).to receive(:render_converge).with(target_hosts)
153
+ subject.perform_run
154
+ end
155
+ end
156
+ end
157
+ end
158
+
159
+ describe "#connect_target" do
160
+ let(:host) { double("TargetHost", config: {}, user: "root" ) }
161
+ let(:reporter) { double("reporter", update: :ok, success: :ok) }
162
+ it "invokes do_connect with correct options" do
163
+ expect(subject).to receive(:do_connect).
164
+ with(host, reporter)
165
+ subject.connect_target(host, reporter)
166
+ end
167
+ end
168
+
169
+ describe "#generate_temp_cookbook" do
170
+ before do
171
+ allow(subject).to receive(:parsed_options).and_return({ cookbook_repo_paths: "/tmp" })
172
+ end
173
+ let(:temp_cookbook) { double("TempCookbook") }
174
+ let(:action) { double("generator", generated_cookbook: temp_cookbook) }
175
+
176
+ context "when a resource is provided" do
177
+ it "gets an action via GenerateTemporaryCookbook.from_options and executes it " do
178
+ expect(ChefApply::Action::GenerateTempCookbook)
179
+ .to receive(:from_options)
180
+ .with(resource_type: "user",
181
+ resource_name: "test", resource_properties: {})
182
+ .and_return(action)
183
+ expect(action).to receive(:run)
184
+ expect(subject.generate_temp_cookbook(%w{user test}, nil)).to eq temp_cookbook
185
+ end
186
+ end
187
+
188
+ context "when a recipe specifier is provided" do
189
+
190
+ it "gets an action via GenerateTemporaryCookbook.from_options and executes it" do
191
+ expect(ChefApply::Action::GenerateTempCookbook)
192
+ .to receive(:from_options)
193
+ .with(recipe_spec: "mycookbook::default", cookbook_repo_paths: "/tmp")
194
+ .and_return(action)
195
+ expect(action).to receive(:run)
196
+ subject.generate_temp_cookbook(["mycookbook::default"], nil)
197
+ end
198
+ end
199
+
200
+ context "when generator posts event:" do
201
+ let(:reporter) { double("reporter") }
202
+ before do
203
+ expect(ChefApply::Action::GenerateTempCookbook)
204
+ .to receive(:from_options)
205
+ .and_return(action)
206
+ allow(action).to receive(:run) { |&block| block.call(event, event_args) }
207
+ end
208
+
209
+ context ":generating" do
210
+ let(:event) { :generating }
211
+ let(:event_args) { nil }
212
+ it "updates message text via reporter" do
213
+ expected_text = ChefApply::CLI::TS.generate_temp_cookbook.generating
214
+ expect(reporter).to receive(:update).with(expected_text)
215
+ subject.generate_temp_cookbook(%w{user jimbo}, reporter)
216
+ end
217
+ end
218
+
219
+ context ":success" do
220
+ let(:event) { :success }
221
+ let(:event_args) { [ temp_cookbook ] }
222
+ it "indicates success via reporter and returns the cookbook" do
223
+ expected_text = ChefApply::CLI::TS.generate_temp_cookbook.success
224
+ expect(reporter).to receive(:success).with(expected_text)
225
+ expect(subject.generate_temp_cookbook(%w{user jimbo}, reporter))
226
+ .to eq temp_cookbook
227
+ end
228
+ end
229
+ end
230
+ end
231
+
232
+ describe "#generate_local_policy" do
233
+ let(:reporter) { double("reporter") }
234
+ let(:action) { double("GenerateLocalPolicy") }
235
+ let(:temp_cookbook) { instance_double("TempCookbook") }
236
+ let(:archive_file_location) { "/temp/archive.gz" }
237
+
238
+ before do
239
+ allow(subject).to receive(:temp_cookbook).and_return temp_cookbook
240
+ allow(action).to receive(:archive_file_location).and_return archive_file_location
241
+ end
242
+ it "creates a GenerateLocalPolicy action and executes it" do
243
+ expect(ChefApply::Action::GenerateLocalPolicy).to receive(:new)
244
+ .with(cookbook: temp_cookbook)
245
+ .and_return(action)
246
+ expect(action).to receive(:run)
247
+ subject.generate_local_policy(reporter)
248
+ end
249
+
250
+ context "when generator posts an event:" do
251
+ before do
252
+ expect(ChefApply::Action::GenerateLocalPolicy).to receive(:new)
253
+ .with(cookbook: temp_cookbook)
254
+ .and_return(action)
255
+ allow(action).to receive(:run) { |&block| block.call(event, event_args) }
256
+ end
257
+
258
+ context ":generating" do
259
+ let(:event) { :generating }
260
+ let(:event_args) { nil }
261
+ let(:expected_msg) { ChefApply::CLI::TS.generate_local_policy.generating }
262
+ it "updates message text correctly via reporter" do
263
+ expect(reporter).to receive(:update).with(expected_msg)
264
+ subject.generate_local_policy(reporter)
265
+ end
266
+
267
+ end
268
+
269
+ context ":exporting" do
270
+ let(:event) { :exporting }
271
+ let(:event_args) { nil }
272
+ let(:expected_msg) { ChefApply::CLI::TS.generate_local_policy.exporting }
273
+ it "updates message text correctly via reporter" do
274
+ expect(reporter).to receive(:update).with(expected_msg)
275
+ subject.generate_local_policy(reporter)
276
+ end
277
+ end
278
+
279
+ context ":success" do
280
+ let(:event) { :success }
281
+ let(:expected_msg) { ChefApply::CLI::TS.generate_local_policy.success }
282
+ let(:event_args) { [archive_file_location] }
283
+ it "indicates success via reporter and returns the archive file location" do
284
+ expect(reporter).to receive(:success).with(expected_msg)
285
+ expect(subject.generate_local_policy(reporter)).to eq archive_file_location
286
+ end
287
+ end
288
+ end
289
+ end
290
+
291
+ describe "#render_cookbook_setup" do
292
+ let(:reporter) { instance_double(ChefApply::StatusReporter) }
293
+ let(:temp_cookbook) { double(ChefApply::TempCookbook) }
294
+ let(:archive_file_location) { "/path/to/archive" }
295
+ let(:args) { [] }
296
+ before do
297
+ allow(ChefApply::UI::Terminal).to receive(:render_job).and_yield(reporter)
298
+ end
299
+
300
+ it "generates the cookbook and local policy" do
301
+ expect(subject).to receive(:generate_temp_cookbook)
302
+ .with(args, reporter).and_return temp_cookbook
303
+ expect(subject).to receive(:generate_local_policy)
304
+ .with(reporter).and_return archive_file_location
305
+ subject.render_cookbook_setup(args)
306
+ end
307
+ end
308
+
309
+ describe "#render_converge" do
310
+
311
+ let(:reporter) { instance_double(ChefApply::StatusReporter) }
312
+ let(:host1) { ChefApply::TargetHost.new("ssh://host1") }
313
+ let(:host2) { ChefApply::TargetHost.new("ssh://host2") }
314
+ let(:cookbook_type) { :resource } # || :recipe
315
+ let(:temp_cookbook) do
316
+ instance_double(ChefApply::TempCookbook,
317
+ descriptor: "resource[name]",
318
+ from: "resource") end
319
+ let(:archive_file_location) { "/path/to/archive" }
320
+
321
+ before do
322
+ allow(subject).to receive(:temp_cookbook).and_return temp_cookbook
323
+ allow(subject).to receive(:archive_file_location).and_return archive_file_location
324
+ expected_header = ChefApply::CLI::TS.converge.header(2, temp_cookbook.descriptor, temp_cookbook.from)
325
+ allow(ChefApply::UI::Terminal).to receive(:render_parallel_jobs) do |header, jobs|
326
+ expect(header).to eq expected_header
327
+ jobs.each { |j| j.run(reporter) }
328
+ end
329
+ end
330
+
331
+ let(:target_hosts) { [host1, host2] }
332
+ it "connects, installs chef, and converges for each target" do
333
+ target_hosts.each do |host|
334
+ expect(subject).to receive(:connect_target).with(host, reporter)
335
+ expect(subject).to receive(:install).with(host, reporter)
336
+ expect(subject).to receive(:converge).with(reporter, archive_file_location, host)
337
+ end
338
+ subject.render_converge(target_hosts)
339
+ end
340
+ end
341
+
342
+ describe "#install" do
343
+ let(:upgrading) { false }
344
+ let(:target_host) { double("targethost", installed_chef_version: "14.0") }
345
+ let(:reporter) { double("reporter") }
346
+ let(:action) do
347
+ double("ChefApply::Actions::InstallChef",
348
+ upgrading?: upgrading,
349
+ version_to_install: "14.0") end
350
+
351
+ it "updates status, gets an InstallChef via instance_for_target and executes it" do
352
+ expect(reporter)
353
+ .to receive(:update)
354
+ .with(ChefApply::CLI::TS.install_chef.verifying)
355
+ expect(ChefApply::Action::InstallChef).to receive(:instance_for_target)
356
+ .with(target_host, check_only: false)
357
+ .and_return action
358
+ expect(action).to receive(:run)
359
+ subject.install(target_host, reporter)
360
+ end
361
+
362
+ context "when generator posts event:" do
363
+ let(:event_args) { nil }
364
+ let(:text_context) { ChefApply::Text.status.install_chef }
365
+
366
+ before do
367
+ allow(ChefApply::Action::InstallChef)
368
+ .to receive(:instance_for_target).and_return action
369
+ allow(action)
370
+ .to receive(:run) { |&block| block.call(event, event_args) }
371
+ allow(reporter)
372
+ .to receive(:update).with(ChefApply::CLI::TS.install_chef.verifying)
373
+ end
374
+
375
+ context ":installing" do
376
+ let(:event) { :installing }
377
+
378
+ context "when installer is upgrading" do
379
+ let(:upgrading) { true }
380
+ it "reports the update correctly" do
381
+ expect(reporter).to receive(:update).with(text_context.upgrading(target_host.installed_chef_version, action.version_to_install))
382
+ subject.install(target_host, reporter)
383
+ end
384
+ end
385
+
386
+ context "when installer is installing clean" do
387
+ let(:upgrading) { false }
388
+ it "reports the update correctly" do
389
+ expect(reporter).to receive(:update).with(text_context.installing(action.version_to_install))
390
+ subject.install(target_host, reporter)
391
+ end
392
+ end
393
+ end
394
+
395
+ context ":uploading" do
396
+ let(:event) { :uploading }
397
+ it "reports the update correctly" do
398
+ expect(reporter).to receive(:update).with(text_context.uploading)
399
+ subject.install(target_host, reporter)
400
+ end
401
+ end
402
+
403
+ context ":downloading" do
404
+ let(:event) { :downloading }
405
+ it "reports the update correctly" do
406
+ expect(reporter).to receive(:update).with(text_context.downloading)
407
+ subject.install(target_host, reporter)
408
+ end
409
+ end
410
+
411
+ context ":already_installed" do
412
+ let(:event) { :already_installed }
413
+ it "reports the update correctly" do
414
+ expect(reporter).to receive(:update).with(text_context.already_present(target_host.installed_chef_version))
415
+ subject.install(target_host, reporter)
416
+ end
417
+ end
418
+
419
+ context ":install_complete" do
420
+ let(:event) { :install_complete }
421
+ context "when installer is upgrading" do
422
+ let(:upgrading) { true }
423
+ it "reports the update correctly" do
424
+ expect(reporter).to receive(:update).with(text_context.upgrade_success(target_host.installed_chef_version,
425
+ action.version_to_install))
426
+ subject.install(target_host, reporter)
427
+ end
428
+ end
429
+
430
+ context "when installer installing clean" do
431
+ let(:upgrading) { false }
432
+ it "reports the update correctly" do
433
+ expect(reporter).to receive(:update).with(text_context.install_success(target_host.installed_chef_version))
434
+ subject.install(target_host, reporter)
435
+ end
436
+ end
437
+ end
438
+ end
439
+ end
440
+ end