chef-apply 0.4.6 → 0.4.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -7
- data/chef-apply.gemspec +3 -3
- data/lib/chef_apply/version.rb +1 -1
- metadata +3 -38
- data/README.md +0 -62
- data/spec/fixtures/custom_config.toml +0 -2
- data/spec/integration/chef-run_spec.rb +0 -41
- data/spec/integration/fixtures/chef_help.out +0 -70
- data/spec/integration/fixtures/chef_version.out +0 -1
- data/spec/integration/spec_helper.rb +0 -55
- data/spec/spec_helper.rb +0 -154
- data/spec/support/matchers/output_to_terminal.rb +0 -36
- data/spec/unit/action/base_spec.rb +0 -60
- data/spec/unit/action/converge_target/ccr_failure_mapper_spec.rb +0 -106
- data/spec/unit/action/converge_target_spec.rb +0 -400
- data/spec/unit/action/generate_local_policy_spec.rb +0 -114
- data/spec/unit/action/generate_temp_cookbook/recipe_lookup_spec.rb +0 -122
- data/spec/unit/action/generate_temp_cookbook/temp_cookbook_spec.rb +0 -198
- data/spec/unit/action/generate_temp_cookbook_spec.rb +0 -73
- data/spec/unit/action/install_chef/minimum_chef_version_spec.rb +0 -90
- data/spec/unit/action/install_chef_spec.rb +0 -164
- data/spec/unit/cli/options_spec.rb +0 -75
- data/spec/unit/cli/validation_spec.rb +0 -81
- data/spec/unit/cli_spec.rb +0 -475
- data/spec/unit/config_spec.rb +0 -70
- data/spec/unit/file_fetcher_spec.rb +0 -40
- data/spec/unit/fixtures/multi-error.out +0 -2
- data/spec/unit/log_spec.rb +0 -37
- data/spec/unit/startup_spec.rb +0 -323
- data/spec/unit/target_host/linux_spec.rb +0 -57
- data/spec/unit/target_host/windows_spec.rb +0 -43
- data/spec/unit/target_host_spec.rb +0 -297
- data/spec/unit/target_resolver_spec.rb +0 -380
- data/spec/unit/telemeter/sender_spec.rb +0 -140
- data/spec/unit/telemeter_spec.rb +0 -191
- data/spec/unit/text/error_translation_spec.rb +0 -109
- data/spec/unit/ui/error_printer_spec.rb +0 -196
- data/spec/unit/ui/terminal_spec.rb +0 -119
- data/spec/unit/version_spec.rb +0 -31
@@ -1,140 +0,0 @@
|
|
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/telemeter/sender"
|
20
|
-
require "chef_apply/config"
|
21
|
-
|
22
|
-
RSpec.describe ChefApply::Telemeter::Sender do
|
23
|
-
let(:session_files) { %w{file1 file2} }
|
24
|
-
let(:enabled_flag) { true }
|
25
|
-
let(:dev_mode) { false }
|
26
|
-
let(:config) { double("config") }
|
27
|
-
|
28
|
-
let(:subject) { ChefApply::Telemeter::Sender.new(session_files) }
|
29
|
-
|
30
|
-
before do
|
31
|
-
allow(config).to receive(:dev).and_return dev_mode
|
32
|
-
allow(ChefApply::Config).to receive(:telemetry).and_return config
|
33
|
-
allow(ChefApply::Telemeter).to receive(:enabled?).and_return enabled_flag
|
34
|
-
# Ensure this is not set for each test:
|
35
|
-
ENV.delete("CHEF_TELEMETRY_ENDPOINT")
|
36
|
-
end
|
37
|
-
|
38
|
-
describe "::start_upload_thread" do
|
39
|
-
let(:sender_mock) { instance_double(ChefApply::Telemeter::Sender) }
|
40
|
-
it "spawns a thread to run the send" do
|
41
|
-
expect(ChefApply::Telemeter::Sender).to receive(:find_session_files).and_return session_files
|
42
|
-
expect(ChefApply::Telemeter::Sender).to receive(:new).with(session_files).and_return sender_mock
|
43
|
-
expect(sender_mock).to receive(:run)
|
44
|
-
expect(::Thread).to receive(:new).and_yield
|
45
|
-
ChefApply::Telemeter::Sender.start_upload_thread
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
describe "#run" do
|
50
|
-
before do
|
51
|
-
expect(subject).to receive(:session_files).and_return session_files
|
52
|
-
end
|
53
|
-
|
54
|
-
context "when telemetry is disabled" do
|
55
|
-
let(:enabled_flag) { false }
|
56
|
-
it "deletes session files without sending" do
|
57
|
-
expect(FileUtils).to receive(:rm_rf).with("file1")
|
58
|
-
expect(FileUtils).to receive(:rm_rf).with("file2")
|
59
|
-
expect(FileUtils).to receive(:rm_rf).with(ChefApply::Config.telemetry_session_file)
|
60
|
-
expect(subject).to_not receive(:process_session)
|
61
|
-
subject.run
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
context "when telemetry is enabled" do
|
66
|
-
context "and telemetry dev mode is true" do
|
67
|
-
let(:dev_mode) { true }
|
68
|
-
let(:session_files) { [] } # Ensure we don't send anything without mocking :allthecalls:
|
69
|
-
context "and a custom telemetry endpoint is not set" do
|
70
|
-
it "configures the environment to submit to the Acceptance telemetry endpoint" do
|
71
|
-
subject.run
|
72
|
-
expect(ENV["CHEF_TELEMETRY_ENDPOINT"]).to eq "https://telemetry-acceptance.chef.io"
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
context "and a custom telemetry endpoint is already set" do
|
77
|
-
before do
|
78
|
-
ENV["CHEF_TELEMETRY_ENDPOINT"] = "https://localhost"
|
79
|
-
end
|
80
|
-
it "should not overwrite the custom value" do
|
81
|
-
subject.run
|
82
|
-
expect(ENV["CHEF_TELEMETRY_ENDPOINT"]).to eq "https://localhost"
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
it "submits the session capture for each session file found" do
|
88
|
-
expect(subject).to receive(:process_session).with("file1")
|
89
|
-
expect(subject).to receive(:process_session).with("file2")
|
90
|
-
expect(FileUtils).to receive(:rm_rf).with(ChefApply::Config.telemetry_session_file)
|
91
|
-
subject.run
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
context "when an error occurrs" do
|
96
|
-
it "logs it" do
|
97
|
-
allow(config).to receive(:enabled?).and_raise("Failed")
|
98
|
-
expect(ChefApply::Log).to receive(:fatal)
|
99
|
-
subject.run
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
describe "::find_session_files" do
|
105
|
-
it "finds all telemetry-payload-*.yml files in the telemetry directory" do
|
106
|
-
expect(ChefApply::Config).to receive(:telemetry_path).and_return("/tmp")
|
107
|
-
expect(Dir).to receive(:glob).with("/tmp/telemetry-payload-*.yml").and_return []
|
108
|
-
ChefApply::Telemeter::Sender.find_session_files
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
describe "process_session" do
|
113
|
-
it "loads the sesion and submits it" do
|
114
|
-
expect(subject).to receive(:load_and_clear_session).with("file1").and_return({ "version" => "1.0.0", "entries" => [] })
|
115
|
-
expect(subject).to receive(:submit_session).with({ "version" => "1.0.0", "entries" => [] })
|
116
|
-
subject.process_session("file1")
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
describe "submit_session" do
|
121
|
-
let(:telemetry) { instance_double("telemetry") }
|
122
|
-
it "removes the telemetry session file and starts a new session, then submits each entry in the session" do
|
123
|
-
expect(ChefApply::Config).to receive(:telemetry_session_file).and_return("/tmp/SESSION_ID")
|
124
|
-
expect(FileUtils).to receive(:rm_rf).with("/tmp/SESSION_ID")
|
125
|
-
expect(Telemetry).to receive(:new).and_return telemetry
|
126
|
-
expect(subject).to receive(:submit_entry).with(telemetry, { "event" => "action1" }, 1, 2)
|
127
|
-
expect(subject).to receive(:submit_entry).with(telemetry, { "event" => "action2" }, 2, 2)
|
128
|
-
subject.submit_session( { "version" => "1.0.0",
|
129
|
-
"entries" => [ { "event" => "action1" }, { "event" => "action2" } ] } )
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
describe "submit_entry" do
|
134
|
-
let(:telemetry) { instance_double("telemetry") }
|
135
|
-
it "submits the entry to telemetry" do
|
136
|
-
expect(telemetry).to receive(:deliver).with("test" => "this")
|
137
|
-
subject.submit_entry(telemetry, { "test" => "this" }, 1, 1)
|
138
|
-
end
|
139
|
-
end
|
140
|
-
end
|
data/spec/unit/telemeter_spec.rb
DELETED
@@ -1,191 +0,0 @@
|
|
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/telemeter"
|
20
|
-
|
21
|
-
RSpec.describe ChefApply::Telemeter do
|
22
|
-
subject { ChefApply::Telemeter.instance }
|
23
|
-
let(:host_platform) { "linux" }
|
24
|
-
|
25
|
-
before do
|
26
|
-
allow(subject).to receive(:host_platform).and_return host_platform
|
27
|
-
end
|
28
|
-
|
29
|
-
context "#commit" do
|
30
|
-
context "when telemetry is enabled" do
|
31
|
-
before do
|
32
|
-
allow(subject).to receive(:enabled?).and_return true
|
33
|
-
end
|
34
|
-
|
35
|
-
it "writes events out and clears the queue" do
|
36
|
-
subject.capture(:test)
|
37
|
-
expect(subject.pending_event_count).to eq 1
|
38
|
-
expect(subject).to receive(:convert_events_to_session)
|
39
|
-
expect(subject).to receive(:write_session)
|
40
|
-
|
41
|
-
subject.commit
|
42
|
-
expect(subject.pending_event_count).to eq 0
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
context "when telemetry is disabled" do
|
47
|
-
before do
|
48
|
-
allow(subject).to receive(:enabled?).and_return false
|
49
|
-
end
|
50
|
-
it "does not write any events and clears the queue" do
|
51
|
-
subject.capture(:test)
|
52
|
-
expect(subject.pending_event_count).to eq 1
|
53
|
-
expect(subject).to_not receive(:convert_events_to_session)
|
54
|
-
|
55
|
-
subject.commit
|
56
|
-
expect(subject.pending_event_count).to eq 0
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
context "#timed_action_capture" do
|
62
|
-
context "when a valid target_host is present" do
|
63
|
-
it "invokes timed_capture with action and valid target data" do
|
64
|
-
target = instance_double("TargetHost",
|
65
|
-
base_os: "windows",
|
66
|
-
version: "10.0.0",
|
67
|
-
architecture: "x86_64",
|
68
|
-
hostname: "My_Host",
|
69
|
-
transport_type: "winrm")
|
70
|
-
action = instance_double("Action::Base", name: "test_action",
|
71
|
-
target_host: target)
|
72
|
-
expected_data = {
|
73
|
-
action: "test_action",
|
74
|
-
target: {
|
75
|
-
platform: {
|
76
|
-
name: "windows",
|
77
|
-
version: "10.0.0",
|
78
|
-
architecture: "x86_64",
|
79
|
-
},
|
80
|
-
hostname_sha1: Digest::SHA1.hexdigest("my_host"),
|
81
|
-
transport_type: "winrm",
|
82
|
-
},
|
83
|
-
}
|
84
|
-
expect(subject).to receive(:timed_capture).with(:action, expected_data)
|
85
|
-
subject.timed_action_capture(action) { :ok }
|
86
|
-
end
|
87
|
-
|
88
|
-
context "when a valid target_host is not present" do
|
89
|
-
it "invokes timed_capture with empty target values" do
|
90
|
-
expected_data = { action: "Base", target: { platform: {},
|
91
|
-
hostname_sha1: nil,
|
92
|
-
transport_type: nil } }
|
93
|
-
expect(subject).to receive(:timed_capture)
|
94
|
-
.with(:action, expected_data)
|
95
|
-
subject.timed_action_capture(
|
96
|
-
ChefApply::Action::Base.new(target_host: nil)
|
97
|
-
) { :ok }
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
context "::enabled?" do
|
104
|
-
let(:enabled_flag) { false }
|
105
|
-
let(:config) { double("config") }
|
106
|
-
before do
|
107
|
-
allow(ChefApply::Config).to receive(:telemetry).and_return(config)
|
108
|
-
allow(config).to receive(:enable).and_return(enabled_flag)
|
109
|
-
end
|
110
|
-
|
111
|
-
context "when config value is enabled" do
|
112
|
-
let(:enabled_flag) { true }
|
113
|
-
context "and CHEF_TELEMETRY_OPT_OUT is not present in env vars" do
|
114
|
-
it "returns false" do
|
115
|
-
ENV.delete("CHEF_TELEMETRY_OPT_OUT")
|
116
|
-
expect(subject.enabled?).to eq true
|
117
|
-
end
|
118
|
-
end
|
119
|
-
context "and CHEF_TELEMETRY_OPT_OUT is present in env vars" do
|
120
|
-
it "returns false" do
|
121
|
-
ENV["CHEF_TELEMETRY_OPT_OUT"] = ""
|
122
|
-
expect(subject.enabled?).to eq false
|
123
|
-
end
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
context "when config value is disabled" do
|
128
|
-
let(:enabled_flag) { false }
|
129
|
-
it "returns false" do
|
130
|
-
expect(subject.enabled?).to eq false
|
131
|
-
end
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
context "#timed_run_capture" do
|
136
|
-
it "invokes timed_capture with run data" do
|
137
|
-
expected_data = { arguments: [ "arg1" ] }
|
138
|
-
expect(subject).to receive(:timed_capture)
|
139
|
-
.with(:run, expected_data)
|
140
|
-
subject.timed_run_capture(["arg1"])
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
context "#timed_capture" do
|
145
|
-
let(:runner) { double("capture_test") }
|
146
|
-
before do
|
147
|
-
expect(subject.pending_event_count).to eq 0
|
148
|
-
end
|
149
|
-
|
150
|
-
it "runs the requested thing and invokes #capture with duration" do
|
151
|
-
expect(runner).to receive(:do_it)
|
152
|
-
expect(subject).to receive(:capture) do |name, data|
|
153
|
-
expect(name).to eq(:do_it_test)
|
154
|
-
expect(data[:duration]).to be > 0.0
|
155
|
-
end
|
156
|
-
subject.timed_capture(:do_it_test) do
|
157
|
-
runner.do_it
|
158
|
-
end
|
159
|
-
end
|
160
|
-
end
|
161
|
-
|
162
|
-
context "#capture" do
|
163
|
-
before do
|
164
|
-
expect(subject.pending_event_count).to eq 0
|
165
|
-
end
|
166
|
-
it "adds the captured event to the session" do
|
167
|
-
subject.capture(:test, {})
|
168
|
-
expect(subject.pending_event_count) == 1
|
169
|
-
end
|
170
|
-
end
|
171
|
-
|
172
|
-
context "#make_event_payload" do
|
173
|
-
before do
|
174
|
-
allow(subject).to receive(:installation_id).and_return "0000"
|
175
|
-
end
|
176
|
-
|
177
|
-
it "adds expected properties" do
|
178
|
-
payload = subject.make_event_payload(:run, { hello: "world" })
|
179
|
-
expected_payload = {
|
180
|
-
event: :run,
|
181
|
-
properties: {
|
182
|
-
installation_id: "0000",
|
183
|
-
run_timestamp: subject.run_timestamp,
|
184
|
-
host_platform: host_platform,
|
185
|
-
event_data: { hello: "world" },
|
186
|
-
},
|
187
|
-
}
|
188
|
-
expect(payload).to eq expected_payload
|
189
|
-
end
|
190
|
-
end
|
191
|
-
end
|
@@ -1,109 +0,0 @@
|
|
1
|
-
#
|
2
|
-
# Copyright:: Copyright (c) 2017 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/text"
|
20
|
-
|
21
|
-
RSpec.describe ChefApply::Text::ErrorTranslation do
|
22
|
-
|
23
|
-
let(:display_defaults) do
|
24
|
-
{
|
25
|
-
decorations: true,
|
26
|
-
header: true,
|
27
|
-
footer: true,
|
28
|
-
stack: false,
|
29
|
-
log: false }
|
30
|
-
end
|
31
|
-
|
32
|
-
let(:error_table) do
|
33
|
-
{ display_defaults: display_defaults,
|
34
|
-
TESTERROR: test_error }
|
35
|
-
end
|
36
|
-
|
37
|
-
let(:test_error_text) { "This is a test error" }
|
38
|
-
let(:test_error) { {} }
|
39
|
-
|
40
|
-
subject { ChefApply::Text::ErrorTranslation }
|
41
|
-
let(:error_mock) do
|
42
|
-
double("R18n::Translated",
|
43
|
-
text: test_error_text )
|
44
|
-
end
|
45
|
-
let(:translation_mock) do
|
46
|
-
double("R18n::Translated",
|
47
|
-
TESTERROR: error_mock,
|
48
|
-
text: test_error_text )
|
49
|
-
end
|
50
|
-
before do
|
51
|
-
# Mock out the R18n portion - our methods care only that the key exists, and
|
52
|
-
# the test focus is on the display metadata.
|
53
|
-
allow(ChefApply::Text).to receive(:errors).and_return translation_mock
|
54
|
-
allow(ChefApply::Text).to receive(:_error_table).and_return(error_table)
|
55
|
-
end
|
56
|
-
|
57
|
-
context "when some display attributes are specified" do
|
58
|
-
let(:test_error) { { display: { stack: true, log: true } } }
|
59
|
-
it "sets display attributes to specified values and defaults remaining" do
|
60
|
-
translation = subject.new("TESTERROR")
|
61
|
-
expect(translation.decorations).to be true
|
62
|
-
expect(translation.header).to be true
|
63
|
-
expect(translation.footer).to be true
|
64
|
-
expect(translation.stack).to be true
|
65
|
-
expect(translation.log).to be true
|
66
|
-
expect(translation.message).to eq test_error_text
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
context "when all display attributes are specified" do
|
71
|
-
let(:test_error) do
|
72
|
-
{ display: { decorations: false, header: false,
|
73
|
-
footer: false, stack: true, log: true } }
|
74
|
-
end
|
75
|
-
it "sets display attributes to specified values with no defaults" do
|
76
|
-
translation = subject.new("TESTERROR")
|
77
|
-
expect(translation.decorations).to be false
|
78
|
-
expect(translation.header).to be false
|
79
|
-
expect(translation.footer).to be false
|
80
|
-
expect(translation.stack).to be true
|
81
|
-
expect(translation.log).to be true
|
82
|
-
expect(translation.message).to eq test_error_text
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
context "when no attributes for an error are specified" do
|
87
|
-
let(:test_error) { {} }
|
88
|
-
it "sets display attribute to default values and references the correct message" do
|
89
|
-
translation = subject.new("TESTERROR")
|
90
|
-
expect(translation.decorations).to be true
|
91
|
-
expect(translation.header).to be true
|
92
|
-
expect(translation.footer).to be true
|
93
|
-
expect(translation.stack).to be false
|
94
|
-
expect(translation.log).to be false
|
95
|
-
expect(translation.message).to eq test_error_text
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
context "when invalid attributes for an error are specified" do
|
100
|
-
let(:test_error) { { display: { bad_value: true } } }
|
101
|
-
it "raises InvalidDisplayAttributes when invalid attributes are specified" do
|
102
|
-
expect { subject.new("TESTERROR") }
|
103
|
-
.to raise_error(ChefApply::Text::ErrorTranslation::InvalidDisplayAttributes) do |e|
|
104
|
-
expect(e.invalid_attrs).to eq({ bad_value: true })
|
105
|
-
end
|
106
|
-
|
107
|
-
end
|
108
|
-
end
|
109
|
-
end
|
@@ -1,196 +0,0 @@
|
|
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/ui/error_printer"
|
20
|
-
require "chef_apply/text/error_translation"
|
21
|
-
require "chef_apply/errors/standard_error_resolver"
|
22
|
-
require "chef_apply/target_host"
|
23
|
-
|
24
|
-
RSpec.describe ChefApply::UI::ErrorPrinter do
|
25
|
-
|
26
|
-
let(:orig_exception) { StandardError.new("test") }
|
27
|
-
let(:target_host) { ChefApply::TargetHost.mock_instance("mock://localhost") }
|
28
|
-
let(:wrapped_exception) { ChefApply::WrappedError.new(orig_exception, target_host) }
|
29
|
-
|
30
|
-
let(:show_footer) { true }
|
31
|
-
let(:show_log) { true }
|
32
|
-
let(:show_stack) { true }
|
33
|
-
let(:has_decorations) { true }
|
34
|
-
let(:show_header) { true }
|
35
|
-
let(:translation_mock) do
|
36
|
-
instance_double("ChefApply::Errors::ErrorTranslation",
|
37
|
-
footer: show_footer,
|
38
|
-
log: show_log,
|
39
|
-
stack: show_stack,
|
40
|
-
header: show_header,
|
41
|
-
decorations: has_decorations)
|
42
|
-
end
|
43
|
-
subject { ChefApply::UI::ErrorPrinter.new(wrapped_exception, nil) }
|
44
|
-
|
45
|
-
before do
|
46
|
-
allow(ChefApply::Text::ErrorTranslation).to receive(:new).and_return translation_mock
|
47
|
-
end
|
48
|
-
|
49
|
-
context "#format_error" do
|
50
|
-
|
51
|
-
context "and the message has decorations" do
|
52
|
-
let(:has_decorations) { true }
|
53
|
-
it "formats the message using the correct method" do
|
54
|
-
expect(subject).to receive(:format_decorated).and_return "decorated"
|
55
|
-
subject.format_error
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
context "and the message does not have decorations" do
|
60
|
-
let(:has_decorations) { false }
|
61
|
-
it "formats the message using the correct method" do
|
62
|
-
expect(subject).to receive(:format_undecorated).and_return "undecorated"
|
63
|
-
subject.format_error
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
context "#format_body" do
|
69
|
-
RC = ChefApply::TargetHost
|
70
|
-
context "when exception is a ChefApply::Error" do
|
71
|
-
let(:result) { RemoteExecResult.new(1, "", "failed") }
|
72
|
-
let(:orig_exception) { RC::RemoteExecutionFailed.new("localhost", "test", result) }
|
73
|
-
it "invokes the right handler" do
|
74
|
-
expect(subject).to receive(:format_workstation_exception)
|
75
|
-
subject.format_body
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
context "when exception is a Train::Error" do
|
80
|
-
# These may expand as we find error-specific messaging we can provide to customers
|
81
|
-
# for more specific train exceptions
|
82
|
-
let(:orig_exception) { Train::Error.new("test") }
|
83
|
-
it "invokes the right handler" do
|
84
|
-
expect(subject).to receive(:format_train_exception)
|
85
|
-
subject.format_body
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
context "when exception is something else" do
|
90
|
-
# These may expand as we find error-specific messaging we can provide to customers
|
91
|
-
# for more specific general exceptions
|
92
|
-
it "invokes the right handler" do
|
93
|
-
expect(subject).to receive(:format_other_exception)
|
94
|
-
subject.format_body
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
context ".show_error" do
|
100
|
-
subject { ChefApply::UI::ErrorPrinter }
|
101
|
-
context "when handling a MultiJobFailure" do
|
102
|
-
it "recognizes it and invokes capture_multiple_failures" do
|
103
|
-
underlying_error = ChefApply::MultiJobFailure.new([])
|
104
|
-
error_to_process = ChefApply::Errors::StandardErrorResolver.wrap_exception(underlying_error)
|
105
|
-
expect(subject).to receive(:capture_multiple_failures).with(underlying_error)
|
106
|
-
subject.show_error(error_to_process)
|
107
|
-
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
context "when an error occurs in error handling" do
|
112
|
-
it "processes the new failure with dump_unexpected_error" do
|
113
|
-
error_to_raise = StandardError.new("this will be raised")
|
114
|
-
error_to_process = ChefApply::Errors::StandardErrorResolver.wrap_exception(StandardError.new("this is being shown"))
|
115
|
-
# Intercept a known call to raise an error
|
116
|
-
expect(ChefApply::UI::Terminal).to receive(:output).and_raise error_to_raise
|
117
|
-
expect(subject).to receive(:dump_unexpected_error).with(error_to_raise)
|
118
|
-
subject.show_error(error_to_process)
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
end
|
123
|
-
|
124
|
-
context ".capture_multiple_failures" do
|
125
|
-
subject { ChefApply::UI::ErrorPrinter }
|
126
|
-
let(:file_content_capture) { StringIO.new }
|
127
|
-
before do
|
128
|
-
allow(ChefApply::Config).to receive(:error_output_path).and_return "/dev/null"
|
129
|
-
allow(File).to receive(:open).with("/dev/null", "w").and_yield(file_content_capture)
|
130
|
-
end
|
131
|
-
|
132
|
-
it "should write a properly formatted error file" do
|
133
|
-
# TODO - add support for test-only i18n content, so that we don't have
|
134
|
-
# to rely on specific known error IDs that may change or be removed,
|
135
|
-
# and arent' directly relevant to the test at hand.
|
136
|
-
job1 = double("Job", target_host: double("TargetHost", hostname: "host1"),
|
137
|
-
exception: ChefApply::Error.new("CHEFUPL005"))
|
138
|
-
job2 = double("Job", target_host: double("TargetHost", hostname: "host2"),
|
139
|
-
exception: StandardError.new("Hello World"))
|
140
|
-
|
141
|
-
expected_content = File.read("spec/unit/fixtures/multi-error.out")
|
142
|
-
multifailure = ChefApply::MultiJobFailure.new([job1, job2] )
|
143
|
-
subject.capture_multiple_failures(multifailure)
|
144
|
-
expect(file_content_capture.string).to eq expected_content
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
context "#format_footer" do
|
149
|
-
let(:formatter) do
|
150
|
-
ChefApply::UI::ErrorPrinter.new(wrapped_exception, nil)
|
151
|
-
end
|
152
|
-
|
153
|
-
subject do
|
154
|
-
lambda { formatter.format_footer }
|
155
|
-
end
|
156
|
-
|
157
|
-
context "when both log and stack wanted" do
|
158
|
-
let(:show_log) { true }
|
159
|
-
let(:show_stack) { true }
|
160
|
-
assert_string_lookup("errors.footer.both")
|
161
|
-
end
|
162
|
-
|
163
|
-
context "when only log is wanted" do
|
164
|
-
let(:show_log) { true }
|
165
|
-
let(:show_stack) { false }
|
166
|
-
assert_string_lookup("errors.footer.log_only")
|
167
|
-
end
|
168
|
-
|
169
|
-
context "when only stack is wanted" do
|
170
|
-
let(:show_log) { false }
|
171
|
-
let(:show_stack) { true }
|
172
|
-
assert_string_lookup("errors.footer.stack_only")
|
173
|
-
end
|
174
|
-
|
175
|
-
context "when neither log nor stack wanted" do
|
176
|
-
let(:show_log) { false }
|
177
|
-
let(:show_stack) { false }
|
178
|
-
assert_string_lookup("errors.footer.neither")
|
179
|
-
end
|
180
|
-
end
|
181
|
-
|
182
|
-
context ".write_backtrace" do
|
183
|
-
let(:inst) { double(ChefApply::UI::ErrorPrinter) }
|
184
|
-
before do
|
185
|
-
allow(ChefApply::UI::ErrorPrinter).to receive(:new).and_return inst
|
186
|
-
end
|
187
|
-
|
188
|
-
let(:orig_args) { %w{test} }
|
189
|
-
it "formats and saves the backtrace" do
|
190
|
-
expect(inst).to receive(:add_backtrace_header).with(anything, orig_args)
|
191
|
-
expect(inst).to receive(:add_formatted_backtrace)
|
192
|
-
expect(inst).to receive(:save_backtrace)
|
193
|
-
ChefApply::UI::ErrorPrinter.write_backtrace(wrapped_exception, orig_args)
|
194
|
-
end
|
195
|
-
end
|
196
|
-
end
|