puppetlabs-onceover 5.0.3 → 5.0.5
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/.gitignore +4 -0
- data/.rubocop_todo.yml +35 -15
- data/CHANGELOG.md +398 -3
- data/Gemfile +21 -1
- data/lib/puppetlabs-onceover/cli/plugins.rb +3 -3
- data/lib/puppetlabs-onceover/controlrepo.rb +8 -7
- data/lib/puppetlabs-onceover/rake_tasks.rb +2 -2
- data/lib/puppetlabs-onceover/vendored_modules.rb +29 -32
- data/lib/puppetlabs-onceover/version.rb +1 -1
- data/spec/fixtures/controlrepos/deploy_test/Puppetfile +3 -0
- data/spec/fixtures/controlrepos/deploy_test/a_dir/nested_file.txt +1 -0
- data/spec/fixtures/controlrepos/deploy_test/a_symlink +1 -0
- data/spec/fixtures/controlrepos/deploy_test/environment.conf +1 -0
- data/spec/fixtures/controlrepos/deploy_test/regular_file.txt +1 -0
- data/spec/puppetlabs-onceover/beaker_spec.rb +293 -0
- data/spec/puppetlabs-onceover/class_spec.rb +89 -0
- data/spec/puppetlabs-onceover/cli/run_spec.rb +80 -0
- data/spec/puppetlabs-onceover/cli/show_spec.rb +67 -0
- data/spec/puppetlabs-onceover/cli_spec.rb +79 -0
- data/spec/puppetlabs-onceover/controlrepo_spec.rb +787 -5
- data/spec/puppetlabs-onceover/deploy_spec.rb +246 -0
- data/spec/puppetlabs-onceover/group_spec.rb +121 -0
- data/spec/puppetlabs-onceover/logger_spec.rb +30 -0
- data/spec/puppetlabs-onceover/node_spec.rb +124 -0
- data/spec/puppetlabs-onceover/rake_tasks_spec.rb +179 -0
- data/spec/puppetlabs-onceover/rspec/formatters_spec.rb +361 -0
- data/spec/puppetlabs-onceover/runner_spec.rb +307 -0
- data/spec/puppetlabs-onceover/test_spec.rb +177 -0
- data/spec/puppetlabs-onceover/testconfig_spec.rb +451 -0
- data/spec/puppetlabs-onceover/vendored_modules_spec.rb +293 -0
- data/spec/puppetlabs-onceover/version_spec.rb +10 -0
- data/spec/spec_helper.rb +8 -0
- metadata +23 -2
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'rake'
|
|
3
|
+
require 'ostruct'
|
|
4
|
+
|
|
5
|
+
describe 'rake_tasks.rb' do
|
|
6
|
+
# SimpleCov/Coverage tracks a given file path's hit-lines against the ISeq
|
|
7
|
+
# produced by the FIRST time it's compiled in this process. Re-`load`ing
|
|
8
|
+
# the file fresh in a before(:each) (once per example) makes every example
|
|
9
|
+
# after the first exercise a *new*, uninstrumented compilation, so real
|
|
10
|
+
# task-body coverage silently reads back as 0. Instead we `load` the file
|
|
11
|
+
# exactly once for the whole group, and use `Rake::Task#reenable` before
|
|
12
|
+
# each invoke so a task can be invoked again in a later example.
|
|
13
|
+
rake_tasks_path = File.expand_path('../../lib/puppetlabs-onceover/rake_tasks.rb', __dir__)
|
|
14
|
+
Rake.application = Rake::Application.new
|
|
15
|
+
load rake_tasks_path
|
|
16
|
+
|
|
17
|
+
before(:each) do
|
|
18
|
+
Rake::Task.tasks.each(&:reenable)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
describe 'generate_fixtures' do
|
|
22
|
+
it 'writes fixtures.yml when it does not already exist' do
|
|
23
|
+
repo = double('repo', root: '/tmp/nonexistent_root_for_test', fixtures: "fixture_content")
|
|
24
|
+
allow(PuppetlabsOnceover::Controlrepo).to receive(:new).and_return(repo)
|
|
25
|
+
allow(File).to receive(:exist?).and_call_original
|
|
26
|
+
allow(File).to receive(:exist?).with(File.expand_path('./.fixtures.yml', repo.root)).and_return(false)
|
|
27
|
+
allow(File).to receive(:write)
|
|
28
|
+
|
|
29
|
+
Rake::Task['generate_fixtures'].invoke
|
|
30
|
+
|
|
31
|
+
expect(File).to have_received(:write).with(File.expand_path('./.fixtures.yml', repo.root), 'fixture_content')
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it 'raises when fixtures.yml already exists' do
|
|
35
|
+
repo = double('repo', root: '/tmp/nonexistent_root_for_test')
|
|
36
|
+
allow(PuppetlabsOnceover::Controlrepo).to receive(:new).and_return(repo)
|
|
37
|
+
allow(File).to receive(:exist?).and_call_original
|
|
38
|
+
allow(File).to receive(:exist?).with(File.expand_path('./.fixtures.yml', repo.root)).and_return(true)
|
|
39
|
+
|
|
40
|
+
expect { Rake::Task['generate_fixtures'].invoke }.to raise_error(/already exits/)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
describe 'hiera_setup' do
|
|
45
|
+
it 'updates the datadir for hash values containing datadir and writes the new config' do
|
|
46
|
+
hiera_config = { defaults: { datadir: 'orig/data' } }
|
|
47
|
+
repo = double('repo')
|
|
48
|
+
allow(repo).to receive(:hiera_config).and_return(hiera_config)
|
|
49
|
+
# Use File.expand_path so these are real OS-native absolute paths on any
|
|
50
|
+
# platform (Windows Pathname#relative_path_from raises "different prefix"
|
|
51
|
+
# when comparing a bare '/some/repo'-style path against one that's been
|
|
52
|
+
# expanded to a drive-letter path).
|
|
53
|
+
allow(repo).to receive(:hiera_config_file).and_return(File.expand_path('/some/repo/hiera.yaml'))
|
|
54
|
+
allow(repo).to receive(:hiera_data).and_return(File.expand_path('/some/repo/data'))
|
|
55
|
+
allow(repo).to receive(:hiera_config=)
|
|
56
|
+
allow(PuppetlabsOnceover::Controlrepo).to receive(:new).and_return(repo)
|
|
57
|
+
|
|
58
|
+
Rake::Task['hiera_setup'].invoke
|
|
59
|
+
|
|
60
|
+
expect(repo).to have_received(:hiera_config=) do |new_config|
|
|
61
|
+
expect(new_config[:defaults][:datadir]).to eq('data')
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it 'leaves non-hash values alone' do
|
|
66
|
+
hiera_config = { version: 5 }
|
|
67
|
+
repo = double('repo')
|
|
68
|
+
allow(repo).to receive(:hiera_config).and_return(hiera_config)
|
|
69
|
+
allow(repo).to receive(:hiera_config_file).and_return('/some/repo/hiera.yaml')
|
|
70
|
+
allow(repo).to receive(:hiera_data).and_return('/some/repo/data')
|
|
71
|
+
allow(repo).to receive(:hiera_config=)
|
|
72
|
+
allow(PuppetlabsOnceover::Controlrepo).to receive(:new).and_return(repo)
|
|
73
|
+
|
|
74
|
+
Rake::Task['hiera_setup'].invoke
|
|
75
|
+
|
|
76
|
+
expect(repo).to have_received(:hiera_config=).with(hiera_config)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
describe 'controlrepo_details' do
|
|
81
|
+
it 'prints the controlrepo to_s' do
|
|
82
|
+
repo = double('repo', to_s: 'REPO DETAILS')
|
|
83
|
+
allow(PuppetlabsOnceover::Controlrepo).to receive(:new).and_return(repo)
|
|
84
|
+
|
|
85
|
+
expect { Rake::Task['controlrepo_details'].invoke }.to output(/REPO DETAILS/).to_stdout
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
describe 'generate_onceover_yaml' do
|
|
90
|
+
it 'reads the controlrepo and renders the controlrepo.yaml.erb template' do
|
|
91
|
+
repo = double('repo', roles: %w[role::web role::db], facts_files: ['/some/repo/facts/centos7.json', '/some/repo/facts/Windows_Server-2019.json'])
|
|
92
|
+
allow(PuppetlabsOnceover::Controlrepo).to receive(:new).and_return(repo)
|
|
93
|
+
|
|
94
|
+
# ERB.new's legacy positional (safe_level, trim_mode) arguments were
|
|
95
|
+
# removed in the bundled erb gem (6.0.7+, Ruby 4.0) -- rake_tasks.rb
|
|
96
|
+
# used to pass them positionally, which raised ArgumentError on that
|
|
97
|
+
# Ruby before this fix. Now that it's fixed (keyword trim_mode:), this
|
|
98
|
+
# asserts the template actually renders real content from the repo
|
|
99
|
+
# double, not just that it "reaches" the call.
|
|
100
|
+
expect { Rake::Task['generate_onceover_yaml'].invoke }.to output(
|
|
101
|
+
a_string_including('role::web', 'role::db', 'centos7', 'Windows_Server-2019', 'Windows_Server-2019')
|
|
102
|
+
).to_stdout
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
describe 'generate_nodesets' do
|
|
107
|
+
it 'writes a nodeset entry for each factset, handling a 404 box lookup' do
|
|
108
|
+
repo = double('repo')
|
|
109
|
+
allow(repo).to receive(:facts).and_return([{ 'os' => { 'family' => 'RedHat' } }])
|
|
110
|
+
allow(repo).to receive(:facts_files).and_return(['/some/repo/facts/centos7.json'])
|
|
111
|
+
allow(PuppetlabsOnceover::Controlrepo).to receive(:new).and_return(repo)
|
|
112
|
+
|
|
113
|
+
require 'puppetlabs-onceover/beaker'
|
|
114
|
+
allow(PuppetlabsOnceover::Beaker).to receive(:facts_to_vagrant_box).and_return('centos/7')
|
|
115
|
+
allow(PuppetlabsOnceover::Beaker).to receive(:facts_to_platform).and_return('el-7-x86_64')
|
|
116
|
+
|
|
117
|
+
http_double = double('http', use_ssl: true, 'use_ssl=': true)
|
|
118
|
+
response = double('response', code: '404', body: '{}')
|
|
119
|
+
allow(Net::HTTP).to receive(:new).and_return(http_double)
|
|
120
|
+
allow(http_double).to receive(:get).and_return(response)
|
|
121
|
+
|
|
122
|
+
# This deprecated task (Beaker removed) has a second, deeper,
|
|
123
|
+
# pre-existing bug beyond the ERB.new Ruby-4.0 incompatibility fixed
|
|
124
|
+
# elsewhere: nodeset.yaml.erb expects a `hosts_hash` local (and a
|
|
125
|
+
# `prefix` local) that the task body never actually defines -- it
|
|
126
|
+
# builds flat locals (node_name/boxname/platform/url/comment_out)
|
|
127
|
+
# instead. Out of scope to redesign a deprecated task here; asserting
|
|
128
|
+
# the real resulting error (and that execution reaches it, covering
|
|
129
|
+
# the "HOSTS:" header and 404 branch first) rather than the old,
|
|
130
|
+
# now-fixed ArgumentError.
|
|
131
|
+
expect { Rake::Task['generate_nodesets'].invoke }.to raise_error(NameError, /hosts_hash/).and output(/HOSTS:/).to_stdout
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
it 'writes a nodeset entry using the virtualbox provider url when the box lookup succeeds' do
|
|
135
|
+
repo = double('repo')
|
|
136
|
+
allow(repo).to receive(:facts).and_return([{ 'os' => { 'family' => 'RedHat' } }])
|
|
137
|
+
allow(repo).to receive(:facts_files).and_return(['/some/repo/facts/centos7.json'])
|
|
138
|
+
allow(PuppetlabsOnceover::Controlrepo).to receive(:new).and_return(repo)
|
|
139
|
+
|
|
140
|
+
require 'puppetlabs-onceover/beaker'
|
|
141
|
+
allow(PuppetlabsOnceover::Beaker).to receive(:facts_to_vagrant_box).and_return('centos/7')
|
|
142
|
+
allow(PuppetlabsOnceover::Beaker).to receive(:facts_to_platform).and_return('el-7-x86_64')
|
|
143
|
+
|
|
144
|
+
box_body = {
|
|
145
|
+
'current_version' => {
|
|
146
|
+
'providers' => [
|
|
147
|
+
{ 'name' => 'virtualbox', 'original_url' => 'https://example.com/box.box' }
|
|
148
|
+
]
|
|
149
|
+
}
|
|
150
|
+
}.to_json
|
|
151
|
+
|
|
152
|
+
http_double = double('http', use_ssl: true, 'use_ssl=': true)
|
|
153
|
+
response = double('response', code: '200', body: box_body)
|
|
154
|
+
allow(Net::HTTP).to receive(:new).and_return(http_double)
|
|
155
|
+
allow(http_double).to receive(:get).and_return(response)
|
|
156
|
+
|
|
157
|
+
# As above: covers the successful (non-404) lookup branch, including
|
|
158
|
+
# the virtualbox provider url extraction, before hitting the same
|
|
159
|
+
# pre-existing hosts_hash/prefix bug described above.
|
|
160
|
+
expect { Rake::Task['generate_nodesets'].invoke }.to raise_error(NameError, /hosts_hash/).and output(/HOSTS:/).to_stdout
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
describe 'generate_vendor_cache' do
|
|
165
|
+
it 'creates a VendoredModules cache for the controlrepo' do
|
|
166
|
+
repo = double('repo', spec_dir: '/some/repo/spec')
|
|
167
|
+
allow(PuppetlabsOnceover::Controlrepo).to receive(:new).with(debug: true).and_return(repo)
|
|
168
|
+
|
|
169
|
+
require 'puppetlabs-onceover/vendored_modules'
|
|
170
|
+
allow(PuppetlabsOnceover::VendoredModules).to receive(:new)
|
|
171
|
+
|
|
172
|
+
Rake::Task['generate_vendor_cache'].invoke
|
|
173
|
+
|
|
174
|
+
expect(PuppetlabsOnceover::VendoredModules).to have_received(:new).with(
|
|
175
|
+
hash_including(repo: repo, force_update: true)
|
|
176
|
+
)
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'stringio'
|
|
3
|
+
require 'tmpdir'
|
|
4
|
+
require 'fileutils'
|
|
5
|
+
require 'yaml'
|
|
6
|
+
require 'puppetlabs-onceover/controlrepo'
|
|
7
|
+
require 'puppetlabs-onceover/rspec/formatters'
|
|
8
|
+
|
|
9
|
+
describe PuppetlabsOnceoverFormatter do
|
|
10
|
+
let(:output) { StringIO.new }
|
|
11
|
+
subject(:formatter) { described_class.new(output) }
|
|
12
|
+
|
|
13
|
+
def group_double(description)
|
|
14
|
+
group = double('group', description: description)
|
|
15
|
+
allow(group).to receive(:parent_groups).and_return([group])
|
|
16
|
+
group
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def stub_world_groups(descriptions)
|
|
20
|
+
groups = descriptions.map { |d| double('group', description: d) }
|
|
21
|
+
allow(RSpec.configuration.world).to receive(:example_groups).and_return(groups)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def failure_double(role:, factset:, raw_error:, description: 'a test', file_path: '/spec/foo_spec.rb', line_number: 5)
|
|
25
|
+
metadata = {
|
|
26
|
+
example_group: {
|
|
27
|
+
description: factset,
|
|
28
|
+
parent_example_group: { description: role }
|
|
29
|
+
},
|
|
30
|
+
execution_result: double('execution_result', exception: RuntimeError.new(raw_error)),
|
|
31
|
+
description: description,
|
|
32
|
+
file_path: file_path,
|
|
33
|
+
line_number: line_number
|
|
34
|
+
}
|
|
35
|
+
double('example', metadata: metadata)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
describe '#example_group_started' do
|
|
39
|
+
context 'when the group is a top-level role' do
|
|
40
|
+
it 'prints the role name and sets previous_role' do
|
|
41
|
+
stub_world_groups(['role::foo', 'role::barbaz'])
|
|
42
|
+
group = group_double('role::foo')
|
|
43
|
+
notification = double('notification', group: group)
|
|
44
|
+
|
|
45
|
+
formatter.example_group_started(notification)
|
|
46
|
+
|
|
47
|
+
expect(output.string).to include('role::foo:')
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it 'does not reprint the same role twice in a row' do
|
|
51
|
+
stub_world_groups(['role::foo'])
|
|
52
|
+
group = group_double('role::foo')
|
|
53
|
+
notification = double('notification', group: group)
|
|
54
|
+
|
|
55
|
+
formatter.example_group_started(notification)
|
|
56
|
+
first_output = output.string
|
|
57
|
+
formatter.example_group_started(notification)
|
|
58
|
+
|
|
59
|
+
expect(output.string).to eq(first_output)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
context 'when the group is nested (not the top-level role)' do
|
|
64
|
+
it "prints '? ' as a placeholder" do
|
|
65
|
+
top = double('top_group')
|
|
66
|
+
group = double('group', description: 'nested context')
|
|
67
|
+
allow(group).to receive(:parent_groups).and_return([top, group])
|
|
68
|
+
notification = double('notification', group: group)
|
|
69
|
+
|
|
70
|
+
formatter.example_group_started(notification)
|
|
71
|
+
|
|
72
|
+
expect(output.string).to eq('? ')
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
describe '#example_passed' do
|
|
78
|
+
it 'writes a green P' do
|
|
79
|
+
formatter.example_passed(double('notification'))
|
|
80
|
+
expect(output.string).to include('P')
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
describe '#example_failed' do
|
|
85
|
+
it 'writes a red F' do
|
|
86
|
+
formatter.example_failed(double('notification'))
|
|
87
|
+
expect(output.string).to include('F')
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
describe '#example_pending' do
|
|
92
|
+
it 'writes a yellow ?' do
|
|
93
|
+
formatter.example_pending(double('notification'))
|
|
94
|
+
expect(output.string).to include('?')
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
describe '#parse_errors' do
|
|
99
|
+
it 'parses a compilation error with a file/line/column location' do
|
|
100
|
+
allow(RSpec.configuration).to receive(:onceover_tempdir).and_return('/tmp/xyz/.onceover')
|
|
101
|
+
allow(RSpec.configuration).to receive(:onceover_root).and_return('/tmp/xyz')
|
|
102
|
+
allow(RSpec.configuration).to receive(:onceover_environmentpath).and_return('etc/puppetlabs/code/environments')
|
|
103
|
+
|
|
104
|
+
raw = 'Some function failed error during compilation: bad value at (file: /tmp/xyz/.onceover/etc/puppetlabs/code/environments/production/site/role/manifests/foo.pp, line: 4, column: 2); more'
|
|
105
|
+
results = formatter.parse_errors(raw)
|
|
106
|
+
expect(results.first[:line]).to eq('4')
|
|
107
|
+
expect(results.first[:column]).to eq('2')
|
|
108
|
+
expect(results.first[:file]).to eq('site/role/manifests/foo.pp')
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
it 'parses a compilation error whose location omits the file' do
|
|
112
|
+
raw = 'error during compilation: bad value at (line: 9, column: 1); trailer'
|
|
113
|
+
results = formatter.parse_errors(raw)
|
|
114
|
+
expect(results.first[:file]).to be_nil
|
|
115
|
+
expect(results.first[:line]).to eq('9')
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
it 'parses a compilation error with no location at all, matching "on node"' do
|
|
119
|
+
raw = 'error during compilation: something bad happened on node foo.example.com'
|
|
120
|
+
results = formatter.parse_errors(raw)
|
|
121
|
+
expect(results.first[:text]).to eq('something bad happened')
|
|
122
|
+
expect(results.first[:file]).to be_nil
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
it 'falls back to the raw string when a compilation error matches neither location pattern' do
|
|
126
|
+
raw = 'error during compilation: nothing structured here whatsoever'
|
|
127
|
+
results = formatter.parse_errors(raw)
|
|
128
|
+
expect(results).to eq([{ text: raw }])
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
it 'falls back to the raw string for a non-compilation error' do
|
|
132
|
+
raw = "NoMethodError: undefined method 'foo' for nil"
|
|
133
|
+
results = formatter.parse_errors(raw)
|
|
134
|
+
expect(results).to eq([{ text: raw }])
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
describe '#calculate_relative_source' do
|
|
139
|
+
before do
|
|
140
|
+
allow(RSpec.configuration).to receive(:onceover_tempdir).and_return('/tmp/xyz/.onceover')
|
|
141
|
+
allow(RSpec.configuration).to receive(:onceover_root).and_return('/tmp/xyz')
|
|
142
|
+
allow(RSpec.configuration).to receive(:onceover_environmentpath).and_return('etc/puppetlabs/code/environments')
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
it 'returns nil when given a nil file' do
|
|
146
|
+
expect(formatter.calculate_relative_source(nil)).to be_nil
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
it 'calculates the path relative to the production environment root' do
|
|
150
|
+
file = '/tmp/xyz/.onceover/etc/puppetlabs/code/environments/production/site/role/manifests/foo.pp'
|
|
151
|
+
expect(formatter.calculate_relative_source(file)).to eq('site/role/manifests/foo.pp')
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
describe '#extract_failure_data' do
|
|
156
|
+
it 'attaches the list of failing factsets to each parsed error' do
|
|
157
|
+
fails = [
|
|
158
|
+
failure_double(role: 'role::foo', factset: 'using fact set centos-7-x86_64', raw_error: 'boom'),
|
|
159
|
+
failure_double(role: 'role::foo', factset: 'using fact set windows-2019-x64', raw_error: 'boom')
|
|
160
|
+
]
|
|
161
|
+
results = formatter.extract_failure_data(fails)
|
|
162
|
+
expect(results.first[:factsets]).to eq(['centos-7-x86_64', 'windows-2019-x64'])
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
describe '#extract_failures' do
|
|
167
|
+
it 'groups failures by role and then by distinct error message' do
|
|
168
|
+
notification = double(
|
|
169
|
+
'notification',
|
|
170
|
+
failed_examples: [
|
|
171
|
+
failure_double(role: 'role::foo', factset: 'using fact set centos-7-x86_64', raw_error: 'boom one'),
|
|
172
|
+
failure_double(role: 'role::foo', factset: 'using fact set windows-2019-x64', raw_error: 'boom two'),
|
|
173
|
+
failure_double(role: 'role::bar', factset: 'using fact set centos-7-x86_64', raw_error: 'boom three')
|
|
174
|
+
]
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
grouped = formatter.extract_failures(notification)
|
|
178
|
+
|
|
179
|
+
expect(grouped.keys).to contain_exactly('role::foo', 'role::bar')
|
|
180
|
+
expect(grouped['role::foo'].size).to eq(2)
|
|
181
|
+
expect(grouped['role::bar'].size).to eq(1)
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
describe '#dump_failures' do
|
|
186
|
+
before do
|
|
187
|
+
allow(RSpec.configuration).to receive(:onceover_tempdir).and_return('/tmp/xyz/.onceover')
|
|
188
|
+
allow(RSpec.configuration).to receive(:onceover_root).and_return('/tmp/xyz')
|
|
189
|
+
allow(RSpec.configuration).to receive(:onceover_environmentpath).and_return('etc/puppetlabs/code/environments')
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
it 'renders the error summary template for each failing role' do
|
|
193
|
+
notification = double(
|
|
194
|
+
'notification',
|
|
195
|
+
failed_examples: [
|
|
196
|
+
failure_double(role: 'role::foo', factset: 'using fact set centos-7-x86_64', raw_error: 'boom')
|
|
197
|
+
]
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
formatter.dump_failures(notification)
|
|
201
|
+
|
|
202
|
+
expect(output.string).to include('role::foo')
|
|
203
|
+
expect(output.string).to include('failed')
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
describe '#longest_group' do
|
|
208
|
+
it 'returns the length of the longest example group description' do
|
|
209
|
+
stub_world_groups(['role::a', 'role::much_longer_name'])
|
|
210
|
+
expect(formatter.longest_group).to eq('role::much_longer_name'.length)
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
describe 'color helper methods' do
|
|
215
|
+
[:class_name, :black, :red, :green, :yellow, :blue, :magenta, :cyan, :white, :bold].each do |method|
|
|
216
|
+
it "##{method} wraps the given text" do
|
|
217
|
+
expect(formatter.public_send(method, 'hello')).to be_a(String)
|
|
218
|
+
expect(formatter.public_send(method, 'hello')).to include('hello')
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
describe PuppetlabsOnceoverFormatterParallel do
|
|
225
|
+
let(:output) { StringIO.new }
|
|
226
|
+
subject(:formatter) { described_class.new(output) }
|
|
227
|
+
|
|
228
|
+
def failure_double(role:, factset:, raw_error:)
|
|
229
|
+
metadata = {
|
|
230
|
+
example_group: {
|
|
231
|
+
description: factset,
|
|
232
|
+
parent_example_group: { description: role }
|
|
233
|
+
},
|
|
234
|
+
execution_result: double('execution_result', exception: RuntimeError.new(raw_error))
|
|
235
|
+
}
|
|
236
|
+
double('example', metadata: metadata)
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
describe '#example_group_started' do
|
|
240
|
+
it 'does nothing' do
|
|
241
|
+
expect { formatter.example_group_started(double('notification')) }.not_to raise_error
|
|
242
|
+
expect(output.string).to eq('')
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
describe '#example_passed' do
|
|
247
|
+
it 'writes and flushes a green P' do
|
|
248
|
+
formatter.example_passed(double('notification'))
|
|
249
|
+
expect(output.string).to include('P')
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
describe '#example_failed' do
|
|
254
|
+
it 'writes and flushes a red F' do
|
|
255
|
+
formatter.example_failed(double('notification'))
|
|
256
|
+
expect(output.string).to include('F')
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
describe '#example_pending' do
|
|
261
|
+
it 'writes and flushes a yellow ?' do
|
|
262
|
+
formatter.example_pending(double('notification'))
|
|
263
|
+
expect(output.string).to include('?')
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
describe '#dump_failures' do
|
|
268
|
+
it 'dumps a yaml file of the failures into the parallel results dir' do
|
|
269
|
+
tempdir = Dir.mktmpdir
|
|
270
|
+
allow(RSpec.configuration).to receive(:onceover_tempdir).and_return(tempdir)
|
|
271
|
+
|
|
272
|
+
notification = double(
|
|
273
|
+
'notification',
|
|
274
|
+
failed_examples: [failure_double(role: 'role::foo', factset: 'using fact set centos-7-x86_64', raw_error: 'boom')]
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
formatter.dump_failures(notification)
|
|
278
|
+
|
|
279
|
+
files = Dir["#{tempdir}/parallel/*.yaml"]
|
|
280
|
+
expect(files.size).to eq(1)
|
|
281
|
+
expect(YAML.load_file(files.first)).to have_key('role::foo')
|
|
282
|
+
|
|
283
|
+
FileUtils.remove_entry(tempdir)
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
describe '#output_results' do
|
|
288
|
+
before do
|
|
289
|
+
allow(RSpec.configuration).to receive(:onceover_tempdir).and_return('/tmp/xyz/.onceover')
|
|
290
|
+
allow(RSpec.configuration).to receive(:onceover_root).and_return('/tmp/xyz')
|
|
291
|
+
allow(RSpec.configuration).to receive(:onceover_environmentpath).and_return('etc/puppetlabs/code/environments')
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
it 'merges all yaml result files, renders them, and deletes the source files' do
|
|
295
|
+
directory = Dir.mktmpdir
|
|
296
|
+
File.write(File.join(directory, 'results-1.yaml'), { 'role::foo' => [{ text: 'boom one' }] }.to_yaml)
|
|
297
|
+
File.write(File.join(directory, 'results-2.yaml'), { 'role::bar' => [{ text: 'boom two' }] }.to_yaml)
|
|
298
|
+
|
|
299
|
+
formatter.output_results(directory)
|
|
300
|
+
|
|
301
|
+
expect(output.string).to include('role::foo')
|
|
302
|
+
expect(output.string).to include('role::bar')
|
|
303
|
+
expect(Dir["#{directory}/*.yaml"]).to be_empty
|
|
304
|
+
|
|
305
|
+
FileUtils.remove_entry(directory)
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
it 'merges values when the same role appears in multiple files' do
|
|
309
|
+
directory = Dir.mktmpdir
|
|
310
|
+
File.write(File.join(directory, 'results-1.yaml'), { 'role::foo' => [{ text: 'boom one' }] }.to_yaml)
|
|
311
|
+
File.write(File.join(directory, 'results-2.yaml'), { 'role::foo' => [{ text: 'boom two' }] }.to_yaml)
|
|
312
|
+
|
|
313
|
+
formatter.output_results(directory)
|
|
314
|
+
|
|
315
|
+
expect(output.string).to include('role::foo')
|
|
316
|
+
|
|
317
|
+
FileUtils.remove_entry(directory)
|
|
318
|
+
end
|
|
319
|
+
end
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
describe FailureCollector do
|
|
323
|
+
let(:tempdir) { Dir.mktmpdir }
|
|
324
|
+
|
|
325
|
+
before do
|
|
326
|
+
allow(RSpec.configuration).to receive(:onceover_tempdir).and_return(tempdir)
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
after { FileUtils.rm_rf(tempdir) }
|
|
330
|
+
|
|
331
|
+
describe '#initialize' do
|
|
332
|
+
it 'touches a failures.out file in the onceover tempdir' do
|
|
333
|
+
described_class.new(StringIO.new)
|
|
334
|
+
expect(File.exist?(File.join(tempdir, 'failures.out'))).to be true
|
|
335
|
+
end
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
describe '#dump_failures' do
|
|
339
|
+
it 'appends details of each failed example to failures.out' do
|
|
340
|
+
collector = described_class.new(StringIO.new)
|
|
341
|
+
|
|
342
|
+
fe = double(
|
|
343
|
+
'failed_example',
|
|
344
|
+
metadata: {
|
|
345
|
+
description: 'does a thing',
|
|
346
|
+
execution_result: double('execution_result', exception: RuntimeError.new('kaboom')),
|
|
347
|
+
file_path: '/spec/foo_spec.rb',
|
|
348
|
+
line_number: 12
|
|
349
|
+
}
|
|
350
|
+
)
|
|
351
|
+
failures = double('failures', failed_examples: [fe])
|
|
352
|
+
|
|
353
|
+
collector.dump_failures(failures)
|
|
354
|
+
|
|
355
|
+
contents = File.read(File.join(tempdir, 'failures.out'))
|
|
356
|
+
expect(contents).to include('does a thing')
|
|
357
|
+
expect(contents).to include('kaboom')
|
|
358
|
+
expect(contents).to include('/spec/foo_spec.rb:12')
|
|
359
|
+
end
|
|
360
|
+
end
|
|
361
|
+
end
|