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.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -0
  3. data/.rubocop_todo.yml +35 -15
  4. data/CHANGELOG.md +398 -3
  5. data/Gemfile +21 -1
  6. data/lib/puppetlabs-onceover/cli/plugins.rb +3 -3
  7. data/lib/puppetlabs-onceover/controlrepo.rb +8 -7
  8. data/lib/puppetlabs-onceover/rake_tasks.rb +2 -2
  9. data/lib/puppetlabs-onceover/vendored_modules.rb +29 -32
  10. data/lib/puppetlabs-onceover/version.rb +1 -1
  11. data/spec/fixtures/controlrepos/deploy_test/Puppetfile +3 -0
  12. data/spec/fixtures/controlrepos/deploy_test/a_dir/nested_file.txt +1 -0
  13. data/spec/fixtures/controlrepos/deploy_test/a_symlink +1 -0
  14. data/spec/fixtures/controlrepos/deploy_test/environment.conf +1 -0
  15. data/spec/fixtures/controlrepos/deploy_test/regular_file.txt +1 -0
  16. data/spec/puppetlabs-onceover/beaker_spec.rb +293 -0
  17. data/spec/puppetlabs-onceover/class_spec.rb +89 -0
  18. data/spec/puppetlabs-onceover/cli/run_spec.rb +80 -0
  19. data/spec/puppetlabs-onceover/cli/show_spec.rb +67 -0
  20. data/spec/puppetlabs-onceover/cli_spec.rb +79 -0
  21. data/spec/puppetlabs-onceover/controlrepo_spec.rb +787 -5
  22. data/spec/puppetlabs-onceover/deploy_spec.rb +246 -0
  23. data/spec/puppetlabs-onceover/group_spec.rb +121 -0
  24. data/spec/puppetlabs-onceover/logger_spec.rb +30 -0
  25. data/spec/puppetlabs-onceover/node_spec.rb +124 -0
  26. data/spec/puppetlabs-onceover/rake_tasks_spec.rb +179 -0
  27. data/spec/puppetlabs-onceover/rspec/formatters_spec.rb +361 -0
  28. data/spec/puppetlabs-onceover/runner_spec.rb +307 -0
  29. data/spec/puppetlabs-onceover/test_spec.rb +177 -0
  30. data/spec/puppetlabs-onceover/testconfig_spec.rb +451 -0
  31. data/spec/puppetlabs-onceover/vendored_modules_spec.rb +293 -0
  32. data/spec/puppetlabs-onceover/version_spec.rb +10 -0
  33. data/spec/spec_helper.rb +8 -0
  34. metadata +23 -2
@@ -0,0 +1,307 @@
1
+ require 'spec_helper'
2
+ require 'puppetlabs-onceover/controlrepo' # side effect: defines top-level #logger
3
+ require 'puppetlabs-onceover/test'
4
+ require 'puppetlabs-onceover/rspec/formatters'
5
+ require 'puppetlabs-onceover/runner'
6
+
7
+ describe PuppetlabsOnceover::Runner do
8
+ let(:repo) do
9
+ double(
10
+ 'repo',
11
+ tempdir: '/tmp/onceover-runner-spec',
12
+ environmentpath: 'etc/puppetlabs/code/environments',
13
+ hiera_config: nil,
14
+ root: '/tmp/onceover-runner-spec-root'
15
+ )
16
+ end
17
+
18
+ def build_config(overrides = {})
19
+ double(
20
+ 'config',
21
+ {
22
+ copy_spec_files: nil,
23
+ write_rakefile: nil,
24
+ write_spec_helper: nil,
25
+ write_spec_helper_acceptance: nil,
26
+ spec_tests: [],
27
+ verify_spec_test: nil,
28
+ run_filters: [],
29
+ write_spec_test: nil,
30
+ acceptance_tests: [],
31
+ verify_acceptance_test: nil,
32
+ write_acceptance_tests: nil,
33
+ create_fixtures_symlinks: nil,
34
+ filter_tags: nil,
35
+ fail_fast: false,
36
+ opts: {},
37
+ formatters: []
38
+ }.merge(overrides)
39
+ )
40
+ end
41
+
42
+ let(:config) { build_config }
43
+
44
+ before do
45
+ # Avoid any real filesystem churn from FileUtils calls in #prepare!
46
+ allow(FileUtils).to receive(:rm_rf)
47
+ allow(FileUtils).to receive(:rm_f)
48
+ allow(FileUtils).to receive(:mkdir_p)
49
+ end
50
+
51
+ describe '#initialize' do
52
+ it 'wraps a single symbol mode into an array' do
53
+ runner = described_class.new(repo, config, :spec)
54
+ expect(runner.instance_variable_get(:@mode)).to eq([:spec])
55
+ end
56
+
57
+ it 'accepts an array of modes as-is' do
58
+ runner = described_class.new(repo, config, [:spec, :acceptance])
59
+ expect(runner.instance_variable_get(:@mode)).to eq([:spec, :acceptance])
60
+ end
61
+
62
+ it 'defaults the mode to spec and acceptance' do
63
+ runner = described_class.new(repo, config)
64
+ expect(runner.instance_variable_get(:@mode)).to eq([:spec, :acceptance])
65
+ end
66
+
67
+ it 'sets an empty command prefix when BUNDLE_GEMFILE is not set' do
68
+ original = ENV.delete('BUNDLE_GEMFILE')
69
+ runner = described_class.new(repo, config)
70
+ expect(runner.instance_variable_get(:@command_prefix)).to eq('')
71
+ ENV['BUNDLE_GEMFILE'] = original if original
72
+ end
73
+
74
+ it 'sets a bundle exec command prefix when BUNDLE_GEMFILE is set' do
75
+ original = ENV.fetch('BUNDLE_GEMFILE', nil)
76
+ ENV['BUNDLE_GEMFILE'] = 'Gemfile'
77
+ runner = described_class.new(repo, config)
78
+ expect(runner.instance_variable_get(:@command_prefix)).to eq('bundle exec ')
79
+ if original
80
+ ENV['BUNDLE_GEMFILE'] = original
81
+ else
82
+ ENV.delete('BUNDLE_GEMFILE')
83
+ end
84
+ end
85
+ end
86
+
87
+ describe '#prepare!' do
88
+ it 'runs the spec test path when mode includes :spec' do
89
+ runner = described_class.new(repo, config, :spec)
90
+
91
+ expect(config).to receive(:spec_tests).at_least(:once).and_return(['sometest'])
92
+ expect(config).to receive(:verify_spec_test).with(repo, 'sometest')
93
+ expect(PuppetlabsOnceover::Test).to receive(:deduplicate).with(['sometest']).and_return(['sometest'])
94
+ expect(config).to receive(:run_filters).with(['sometest']).and_return(['sometest'])
95
+ expect(config).to receive(:write_spec_test).with("#{repo.tempdir}/spec/classes", 'sometest')
96
+ expect(config).not_to receive(:acceptance_tests)
97
+
98
+ runner.prepare!
99
+ end
100
+
101
+ it 'runs the acceptance test path when mode includes :acceptance' do
102
+ runner = described_class.new(repo, config, :acceptance)
103
+
104
+ expect(config).to receive(:acceptance_tests).at_least(:once).and_return(['sometest'])
105
+ expect(config).to receive(:verify_acceptance_test).with(repo, 'sometest')
106
+ expect(PuppetlabsOnceover::Test).to receive(:deduplicate).with(['sometest']).and_return(['sometest'])
107
+ expect(config).to receive(:run_filters).with(['sometest']).and_return(['sometest'])
108
+ expect(config).to receive(:write_acceptance_tests).with("#{repo.tempdir}/spec/acceptance", ['sometest'])
109
+ expect(config).not_to receive(:spec_tests)
110
+
111
+ runner.prepare!
112
+ end
113
+
114
+ it 'runs neither test path when mode is empty' do
115
+ runner = described_class.new(repo, config, [])
116
+
117
+ expect(config).not_to receive(:spec_tests)
118
+ expect(config).not_to receive(:acceptance_tests)
119
+
120
+ runner.prepare!
121
+ end
122
+
123
+ it 'skips the hiera rewrite when hiera_config is nil' do
124
+ runner = described_class.new(repo, config, [])
125
+ expect(File).not_to receive(:write)
126
+ runner.prepare!
127
+ end
128
+
129
+ it 'rewrites a plain (non-hash) hiera_config setting untouched and writes hiera.yaml' do
130
+ hiera_repo = double(
131
+ 'repo',
132
+ tempdir: '/tmp/onceover-runner-spec',
133
+ environmentpath: 'etc/puppetlabs/code/environments',
134
+ hiera_config: { 'version' => 5 },
135
+ root: '/tmp/onceover-runner-spec-root'
136
+ )
137
+ runner = described_class.new(hiera_repo, config, [])
138
+
139
+ expect(File).to receive(:write).with(
140
+ "#{hiera_repo.tempdir}/#{hiera_repo.environmentpath}/production/hiera.yaml",
141
+ { 'version' => 5 }.to_yaml
142
+ )
143
+
144
+ runner.prepare!
145
+ end
146
+
147
+ it 'rewrites the datadir of a hierarchy hash entry to point at the tempdir' do
148
+ hiera_config = {
149
+ 'hierarchy' => { datadir: 'hieradata' }
150
+ }
151
+ hiera_repo = double(
152
+ 'repo',
153
+ tempdir: '/tmp/onceover-runner-spec',
154
+ environmentpath: 'etc/puppetlabs/code/environments',
155
+ hiera_config: hiera_config,
156
+ root: '/tmp/onceover-runner-spec-root'
157
+ )
158
+ runner = described_class.new(hiera_repo, config, [])
159
+
160
+ expected_datadir = "#{hiera_repo.tempdir}/#{hiera_repo.environmentpath}/production/hieradata"
161
+
162
+ expect(File).to receive(:write) do |path, contents|
163
+ expect(path).to eq("#{hiera_repo.tempdir}/#{hiera_repo.environmentpath}/production/hiera.yaml")
164
+ expect(contents).to include(expected_datadir)
165
+ end
166
+
167
+ runner.prepare!
168
+ end
169
+
170
+ it 'leaves a hash entry without a :datadir key untouched' do
171
+ hiera_config = {
172
+ 'hierarchy' => { other_key: 'value' }
173
+ }
174
+ hiera_repo = double(
175
+ 'repo',
176
+ tempdir: '/tmp/onceover-runner-spec',
177
+ environmentpath: 'etc/puppetlabs/code/environments',
178
+ hiera_config: hiera_config,
179
+ root: '/tmp/onceover-runner-spec-root'
180
+ )
181
+ runner = described_class.new(hiera_repo, config, [])
182
+
183
+ expect(File).to receive(:write) do |_path, contents|
184
+ expect(contents).not_to include('/tmp/onceover-runner-spec/etc')
185
+ end
186
+
187
+ runner.prepare!
188
+ end
189
+ end
190
+
191
+ describe '#run_spec!' do
192
+ let(:backticks_double) do
193
+ double('backticks_runner', run: double('process', join: double('joined', status: double('status', exitstatus: 0))))
194
+ end
195
+
196
+ before do
197
+ allow(Backticks::Runner).to receive(:new).and_return(backticks_double)
198
+ allow(Dir).to receive(:chdir).and_yield
199
+ allow($stderr).to receive(:isatty).and_return(false)
200
+ end
201
+
202
+ it 'runs rake spec:standalone in standalone mode and exits with the status code' do
203
+ runner = described_class.new(repo, config, :spec)
204
+ logger.level = :info
205
+
206
+ expect(runner).to receive(:exit).with(0)
207
+ runner.run_spec!
208
+ end
209
+
210
+ it 'runs rake parallel_spec when opts[:parallel] is set' do
211
+ parallel_config = build_config(opts: { parallel: true })
212
+ runner = described_class.new(repo, parallel_config, :spec)
213
+ logger.level = :info
214
+
215
+ expect(runner).to receive(:run_command).with(anything, 'rake', 'parallel_spec').and_call_original
216
+ allow(runner).to receive(:exit)
217
+ runner.run_spec!
218
+ end
219
+
220
+ it 'skips the RUBYOPT warning-suppression dance when logger level is debug (zero)' do
221
+ runner = described_class.new(repo, config, :spec)
222
+ logger.level = :debug
223
+
224
+ previous = ENV.fetch('RUBYOPT', nil)
225
+ expect(runner).to receive(:exit).with(0)
226
+ runner.run_spec!
227
+ expect(ENV.fetch('RUBYOPT', nil)).to eq(previous)
228
+ logger.level = :info
229
+ end
230
+
231
+ it 'appends filter tags and fail-fast to CI_SPEC_OPTIONS when configured' do
232
+ tagged_config = build_config(filter_tags: ['sometag'], fail_fast: true)
233
+ runner = described_class.new(repo, tagged_config, :spec)
234
+ logger.level = :info
235
+
236
+ allow(runner).to receive(:exit)
237
+ runner.run_spec!
238
+ expect(ENV.fetch('CI_SPEC_OPTIONS', nil)).to include('--tag sometag')
239
+ expect(ENV.fetch('CI_SPEC_OPTIONS', nil)).to include('--fail-fast')
240
+ ENV.delete('CI_SPEC_OPTIONS')
241
+ end
242
+
243
+ it 'prints a parallel summary when the parallel formatter is configured' do
244
+ formatter_config = build_config(formatters: ['PuppetlabsOnceoverFormatterParallel'])
245
+ runner = described_class.new(repo, formatter_config, :spec)
246
+ logger.level = :info
247
+
248
+ formatter_double = double('formatter', output_results: nil)
249
+ expect(PuppetlabsOnceoverFormatterParallel).to receive(:new).with($stdout).and_return(formatter_double)
250
+ expect(formatter_double).to receive(:output_results).with("#{repo.tempdir}/parallel")
251
+
252
+ allow(runner).to receive(:exit)
253
+ runner.run_spec!
254
+ end
255
+ end
256
+
257
+ describe '#run_acceptance!' do
258
+ let(:backticks_double) do
259
+ double('backticks_runner', run: double('process', join: double('joined', status: double('status', exitstatus: 1))))
260
+ end
261
+
262
+ before do
263
+ allow(Backticks::Runner).to receive(:new).and_return(backticks_double)
264
+ allow(Dir).to receive(:chdir).and_yield
265
+ allow($stderr).to receive(:isatty).and_return(false)
266
+ end
267
+
268
+ it 'warns about deprecation and runs rake acceptance' do
269
+ # NOTE: `result` in the source is assigned only inside the Dir.chdir
270
+ # block and then read again after the block returns -- that's normal
271
+ # Ruby block-local scoping, so `result` is actually undefined at the
272
+ # `exit result.status.exitstatus` line and this method always raises
273
+ # NameError there in real usage. That's a pre-existing bug in
274
+ # lib/puppetlabs-onceover/runner.rb, not something we can cover
275
+ # without changing production behavior, so we assert the crash
276
+ # instead of the (unreachable) exit call.
277
+ runner = described_class.new(repo, config, :acceptance)
278
+
279
+ expect(runner).to receive(:warn).with(/DEPRECATION/)
280
+ expect { runner.run_acceptance! }.to raise_error(NameError, /result/)
281
+ end
282
+ end
283
+
284
+ describe '#run_command' do
285
+ it 'sets STDERR raw when a tty and cooked afterwards' do
286
+ runner = described_class.new(repo, config, :spec)
287
+ backticks_double = double('backticks_runner', run: double('process', join: double('joined')))
288
+ allow(Backticks::Runner).to receive(:new).and_return(backticks_double)
289
+ allow($stderr).to receive(:isatty).and_return(true)
290
+ expect($stderr).to receive(:raw!)
291
+ expect($stderr).to receive(:cooked!)
292
+
293
+ runner.run_command('rake', 'spec:standalone')
294
+ end
295
+
296
+ it 'does not touch STDERR raw/cooked state when not a tty' do
297
+ runner = described_class.new(repo, config, :spec)
298
+ backticks_double = double('backticks_runner', run: double('process', join: double('joined')))
299
+ allow(Backticks::Runner).to receive(:new).and_return(backticks_double)
300
+ allow($stderr).to receive(:isatty).and_return(false)
301
+ expect($stderr).not_to receive(:raw!)
302
+ expect($stderr).not_to receive(:cooked!)
303
+
304
+ runner.run_command('rake', 'spec:standalone')
305
+ end
306
+ end
307
+ end
@@ -0,0 +1,177 @@
1
+ require 'spec_helper'
2
+ require 'puppetlabs-onceover/test'
3
+ require 'puppetlabs-onceover/class'
4
+ require 'puppetlabs-onceover/node'
5
+ require 'puppetlabs-onceover/group'
6
+ require 'puppetlabs-onceover/testconfig'
7
+ require 'puppetlabs-onceover/controlrepo'
8
+
9
+ describe "PuppetlabsOnceover::Test" do
10
+ before(:each) do
11
+ PuppetlabsOnceover::Class.class_variable_set(:@@all, [])
12
+ PuppetlabsOnceover::Node.class_variable_set(:@@all, [])
13
+ PuppetlabsOnceover::Group.class_variable_set(:@@all, [])
14
+ PuppetlabsOnceover::Test.class_variable_set(:@@all, [])
15
+ logger.level = :fatal
16
+ PuppetlabsOnceover::Controlrepo.new(path: 'spec/fixtures/controlrepos/factsets')
17
+ end
18
+
19
+ let(:node_a) { PuppetlabsOnceover::Node.new('node_a') }
20
+ let(:node_b) { PuppetlabsOnceover::Node.new('node_b') }
21
+ let(:class_a) { PuppetlabsOnceover::Class.new('role::a') }
22
+ let(:class_b) { PuppetlabsOnceover::Class.new('role::b') }
23
+
24
+ describe "#initialize" do
25
+ it "merges in the default test_config (check_idempotency/runs_before_idempotency)" do
26
+ test = PuppetlabsOnceover::Test.new(node_a.name, class_a, {})
27
+ expect(test.test_config['check_idempotency']).to eq(true)
28
+ expect(test.test_config['runs_before_idempotency']).to eq(1)
29
+ end
30
+
31
+ it "lets explicit test_config values override the defaults" do
32
+ test = PuppetlabsOnceover::Test.new(node_a.name, class_a, { 'check_idempotency' => false })
33
+ expect(test.test_config['check_idempotency']).to eq(false)
34
+ end
35
+
36
+ it "removes 'classes' from the stored test_config" do
37
+ test = PuppetlabsOnceover::Test.new(node_a.name, class_a, { 'classes' => 'whatever' })
38
+ expect(test.test_config).not_to have_key('classes')
39
+ end
40
+
41
+ it "normalizes a single tag string into an array" do
42
+ test = PuppetlabsOnceover::Test.new(node_a.name, class_a, { 'tags' => 'sometag' })
43
+ expect(test.tags).to eq(['sometag'])
44
+ end
45
+
46
+ it "leaves an array of tags as-is" do
47
+ test = PuppetlabsOnceover::Test.new(node_a.name, class_a, { 'tags' => %w[a b] })
48
+ expect(test.tags).to eq(%w[a b])
49
+ end
50
+
51
+ it "defaults tags to an empty array when absent" do
52
+ test = PuppetlabsOnceover::Test.new(node_a.name, class_a, {})
53
+ expect(test.tags).to eq([])
54
+ end
55
+
56
+ context "resolving the 'on_this' (node) argument" do
57
+ it "accepts a group name and expands it to its member nodes" do
58
+ PuppetlabsOnceover::Group.new('nodegroup', [node_a, node_b])
59
+ test = PuppetlabsOnceover::Test.new('nodegroup', class_a, {})
60
+ expect(test.nodes).to eq([node_a, node_b])
61
+ end
62
+
63
+ it "accepts a plain node name" do
64
+ test = PuppetlabsOnceover::Test.new(node_a.name, class_a, {})
65
+ expect(test.nodes).to eq([node_a])
66
+ end
67
+
68
+ it "raises when the node/group cannot be found" do
69
+ expect do
70
+ PuppetlabsOnceover::Test.new('no_such_node_or_group', class_a, {})
71
+ end.to raise_error(RuntimeError, /was not found in the list of nodes or groups/)
72
+ end
73
+ end
74
+
75
+ context "resolving the 'test_this' (classes) argument" do
76
+ it "accepts a group name and expands it to its member classes" do
77
+ PuppetlabsOnceover::Group.new('classgroup', [class_a, class_b])
78
+ test = PuppetlabsOnceover::Test.new(node_a.name, 'classgroup', {})
79
+ expect(test.classes).to eq([class_a, class_b])
80
+ end
81
+
82
+ it "accepts a plain class name" do
83
+ test = PuppetlabsOnceover::Test.new(node_a.name, class_a.name, {})
84
+ expect(test.classes).to eq([class_a])
85
+ end
86
+
87
+ it "raises when given a string that matches neither a class nor a group" do
88
+ expect do
89
+ PuppetlabsOnceover::Test.new(node_a.name, 'no_such_class_or_group', {})
90
+ end.to raise_error(RuntimeError, /was not found in the list of classes or groups/)
91
+ end
92
+
93
+ it "accepts a subtractive include/exclude hash" do
94
+ PuppetlabsOnceover::Group.new('all_classes', [class_a, class_b])
95
+ PuppetlabsOnceover::Group.new('excluded', [class_b])
96
+ test = PuppetlabsOnceover::Test.new(node_a.name, { 'include' => 'all_classes', 'exclude' => 'excluded' }, {})
97
+ expect(test.classes).to eq([class_a])
98
+ end
99
+
100
+ it "accepts a Class object directly" do
101
+ test = PuppetlabsOnceover::Test.new(node_a.name, class_a, {})
102
+ expect(test.classes).to eq([class_a])
103
+ end
104
+ end
105
+ end
106
+
107
+ describe "#eql?" do
108
+ it "is true when both nodes and classes match once sorted" do
109
+ t1 = PuppetlabsOnceover::Test.new(node_a.name, class_a, {})
110
+ t2 = PuppetlabsOnceover::Test.new(node_a.name, class_a, {})
111
+ expect(t1.eql?(t2)).to eq(true)
112
+ end
113
+
114
+ it "is false when the classes differ" do
115
+ t1 = PuppetlabsOnceover::Test.new(node_a.name, class_a, {})
116
+ t2 = PuppetlabsOnceover::Test.new(node_a.name, class_b, {})
117
+ expect(t1.eql?(t2)).to eq(false)
118
+ end
119
+ end
120
+
121
+ describe "#to_s" do
122
+ it "uses the singular class and node name when there is only one of each" do
123
+ test = PuppetlabsOnceover::Test.new(node_a.name, class_a, {})
124
+ expect(test.to_s).to eq("role__a_on_node_a")
125
+ end
126
+
127
+ it "summarizes as N_classes when there are multiple classes" do
128
+ PuppetlabsOnceover::Group.new('classgroup', [class_a, class_b])
129
+ test = PuppetlabsOnceover::Test.new(node_a.name, 'classgroup', {})
130
+ expect(test.to_s).to eq("2_classes_on_node_a")
131
+ end
132
+
133
+ it "summarizes as N_nodes when there are multiple nodes" do
134
+ PuppetlabsOnceover::Group.new('nodegroup', [node_a, node_b])
135
+ test = PuppetlabsOnceover::Test.new('nodegroup', class_a, {})
136
+ expect(test.to_s).to eq("role__a_on_2_nodes")
137
+ end
138
+ end
139
+
140
+ describe ".deduplicate" do
141
+ it "merges duplicate node/class combinations into a single test, keeping non-default config" do
142
+ t1 = PuppetlabsOnceover::Test.new(node_a.name, class_a, {})
143
+ t2 = PuppetlabsOnceover::Test.new(node_a.name, class_a, { 'check_idempotency' => false })
144
+
145
+ deduped = PuppetlabsOnceover::Test.deduplicate([t1, t2])
146
+ expect(deduped.length).to eq(1)
147
+ expect(deduped.first.test_config['check_idempotency']).to eq(false)
148
+ end
149
+
150
+ it "leaves distinct node/class combinations as separate tests" do
151
+ t1 = PuppetlabsOnceover::Test.new(node_a.name, class_a, {})
152
+ t2 = PuppetlabsOnceover::Test.new(node_b.name, class_b, {})
153
+
154
+ deduped = PuppetlabsOnceover::Test.deduplicate([t1, t2])
155
+ expect(deduped.length).to eq(2)
156
+ end
157
+
158
+ it "handles tests with multiple nodes/classes by expanding every combination" do
159
+ PuppetlabsOnceover::Group.new('nodegroup', [node_a, node_b])
160
+ PuppetlabsOnceover::Group.new('classgroup', [class_a, class_b])
161
+ t1 = PuppetlabsOnceover::Test.new('nodegroup', 'classgroup', {})
162
+
163
+ deduped = PuppetlabsOnceover::Test.deduplicate([t1])
164
+ # 2 nodes x 2 classes = 4 distinct combinations
165
+ expect(deduped.length).to eq(4)
166
+ end
167
+ end
168
+
169
+ describe ".all" do
170
+ # Unlike Class/Node/Group, Test#initialize never appends `self` to @@all,
171
+ # so .all is always empty in practice -- documenting actual behavior here.
172
+ it "returns the (always empty) @@all class variable, since #initialize never appends to it" do
173
+ PuppetlabsOnceover::Test.new(node_a.name, class_a, {})
174
+ expect(PuppetlabsOnceover::Test.all).to eq([])
175
+ end
176
+ end
177
+ end