puppet 7.13.1-universal-darwin → 7.16.0-universal-darwin

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/CODEOWNERS +1 -1
  3. data/Gemfile +1 -1
  4. data/Gemfile.lock +30 -30
  5. data/lib/puppet/application/lookup.rb +25 -23
  6. data/lib/puppet/configurer.rb +8 -14
  7. data/lib/puppet/defaults.rb +11 -1
  8. data/lib/puppet/face/generate.rb +2 -0
  9. data/lib/puppet/functions/next.rb +18 -1
  10. data/lib/puppet/functions/tree_each.rb +0 -1
  11. data/lib/puppet/generate/type.rb +9 -0
  12. data/lib/puppet/http/client.rb +1 -1
  13. data/lib/puppet/node.rb +1 -1
  14. data/lib/puppet/resource/type_collection.rb +21 -17
  15. data/lib/puppet/ssl/ssl_provider.rb +10 -7
  16. data/lib/puppet/type/exec.rb +1 -1
  17. data/lib/puppet/type/user.rb +1 -1
  18. data/lib/puppet/util/monkey_patches.rb +0 -2
  19. data/lib/puppet/util/yaml.rb +5 -1
  20. data/lib/puppet/util.rb +1 -0
  21. data/lib/puppet/version.rb +1 -1
  22. data/locales/puppet.pot +5 -9763
  23. data/man/man5/puppet.conf.5 +13 -2
  24. data/man/man8/puppet-agent.8 +1 -1
  25. data/man/man8/puppet-apply.8 +1 -1
  26. data/man/man8/puppet-catalog.8 +1 -1
  27. data/man/man8/puppet-config.8 +1 -1
  28. data/man/man8/puppet-describe.8 +1 -1
  29. data/man/man8/puppet-device.8 +1 -1
  30. data/man/man8/puppet-doc.8 +1 -1
  31. data/man/man8/puppet-epp.8 +1 -1
  32. data/man/man8/puppet-facts.8 +1 -1
  33. data/man/man8/puppet-filebucket.8 +1 -1
  34. data/man/man8/puppet-generate.8 +1 -1
  35. data/man/man8/puppet-help.8 +1 -1
  36. data/man/man8/puppet-lookup.8 +1 -1
  37. data/man/man8/puppet-module.8 +1 -1
  38. data/man/man8/puppet-node.8 +1 -1
  39. data/man/man8/puppet-parser.8 +1 -1
  40. data/man/man8/puppet-plugin.8 +1 -1
  41. data/man/man8/puppet-report.8 +1 -1
  42. data/man/man8/puppet-resource.8 +1 -1
  43. data/man/man8/puppet-script.8 +1 -1
  44. data/man/man8/puppet-ssl.8 +1 -1
  45. data/man/man8/puppet.8 +2 -2
  46. data/spec/integration/application/lookup_spec.rb +65 -57
  47. data/spec/integration/application/resource_spec.rb +6 -2
  48. data/spec/integration/http/client_spec.rb +30 -0
  49. data/spec/unit/configurer_spec.rb +124 -61
  50. data/spec/unit/confiner_spec.rb +6 -6
  51. data/spec/unit/face/generate_spec.rb +64 -0
  52. data/spec/unit/node_spec.rb +6 -0
  53. data/spec/unit/type/user_spec.rb +67 -0
  54. data/spec/unit/util/windows_spec.rb +23 -0
  55. metadata +5 -3
@@ -9,9 +9,6 @@ describe Puppet::Configurer do
9
9
  Puppet[:report] = true
10
10
 
11
11
  catalog.add_resource(resource)
12
- allow_any_instance_of(described_class).to(
13
- receive(:valid_server_environment?).and_return(true)
14
- )
15
12
 
16
13
  Puppet[:lastrunfile] = file_containing('last_run_summary.yaml', <<~SUMMARY)
17
14
  ---
@@ -78,10 +75,44 @@ describe Puppet::Configurer do
78
75
  end
79
76
  end
80
77
 
78
+ describe "when executing a catalog run without stubbing valid_server_environment?" do
79
+ before do
80
+ Puppet::Resource::Catalog.indirection.terminus_class = :rest
81
+ allow(Puppet::Resource::Catalog.indirection).to receive(:find).and_return(catalog)
82
+ end
83
+
84
+ it 'skips initial plugin sync if environment is not found and no strict_environment_mode' do
85
+ body = "{\"message\":\"Not Found: Could not find environment 'fasdfad'\",\"issue_kind\":\"RUNTIME_ERROR\"}"
86
+ stub_request(:get, %r{/puppet/v3/file_metadatas/plugins?}).to_return(
87
+ status: 404, body: body, headers: {'Content-Type' => 'application/json'}
88
+ )
89
+
90
+ configurer.run(:pluginsync => true)
91
+
92
+ expect(@logs).to include(an_object_having_attributes(level: :notice, message: %r{Environment 'production' not found on server, skipping initial pluginsync.}))
93
+ expect(@logs).to include(an_object_having_attributes(level: :notice, message: /Applied catalog in .* seconds/))
94
+ end
95
+
96
+ it 'if strict_environment_mode is set and environment is not found, aborts the puppet run' do
97
+ Puppet[:strict_environment_mode] = true
98
+ body = "{\"message\":\"Not Found: Could not find environment 'fasdfad'\",\"issue_kind\":\"RUNTIME_ERROR\"}"
99
+ stub_request(:get, %r{/puppet/v3/file_metadatas/plugins?}).to_return(
100
+ status: 404, body: body, headers: {'Content-Type' => 'application/json'}
101
+ )
102
+
103
+ configurer.run(:pluginsync => true)
104
+
105
+ expect(@logs).to include(an_object_having_attributes(level: :err, message: %r{Failed to apply catalog: Environment 'production' not found on server, aborting run.}))
106
+ end
107
+ end
108
+
81
109
  describe "when executing a catalog run" do
82
110
  before do
83
111
  Puppet::Resource::Catalog.indirection.terminus_class = :rest
84
112
  allow(Puppet::Resource::Catalog.indirection).to receive(:find).and_return(catalog)
113
+ allow_any_instance_of(described_class).to(
114
+ receive(:valid_server_environment?).and_return(true)
115
+ )
85
116
  end
86
117
 
87
118
  it "downloads plugins when told" do
@@ -1251,88 +1282,120 @@ describe Puppet::Configurer do
1251
1282
  converged_environment: #{last_server_specified_environment}
1252
1283
  run_mode: agent
1253
1284
  SUMMARY
1285
+ end
1254
1286
 
1255
- expect(Puppet::Node.indirection).not_to receive(:find)
1287
+ describe "when the use_last_environment is set to true" do
1288
+ before do
1289
+ expect(Puppet::Node.indirection).not_to receive(:find)
1256
1290
  .with(anything, hash_including(:ignore_cache => true, :fail_on_404 => true))
1257
- end
1291
+ end
1258
1292
 
1259
- it "prefers the environment set via cli" do
1260
- Puppet.settings.handlearg('--environment', 'usethis')
1261
- configurer.run
1293
+ it "prefers the environment set via cli" do
1294
+ Puppet.settings.handlearg('--environment', 'usethis')
1295
+ configurer.run
1262
1296
 
1263
- expect(configurer.environment).to eq('usethis')
1264
- end
1297
+ expect(configurer.environment).to eq('usethis')
1298
+ end
1265
1299
 
1266
- it "prefers the environment set via lastrunfile over config" do
1267
- FileUtils.mkdir_p(Puppet[:confdir])
1268
- set_puppet_conf(Puppet[:confdir], <<~CONF)
1269
- [main]
1270
- environment = usethis
1271
- lastrunfile = #{Puppet[:lastrunfile]}
1272
- CONF
1300
+ it "prefers the environment set via lastrunfile over config" do
1301
+ FileUtils.mkdir_p(Puppet[:confdir])
1302
+ set_puppet_conf(Puppet[:confdir], <<~CONF)
1303
+ [main]
1304
+ environment = usethis
1305
+ lastrunfile = #{Puppet[:lastrunfile]}
1306
+ CONF
1273
1307
 
1274
- Puppet.initialize_settings
1275
- configurer.run
1308
+ Puppet.initialize_settings
1309
+ configurer.run
1276
1310
 
1277
- expect(configurer.environment).to eq(last_server_specified_environment)
1278
- end
1311
+ expect(configurer.environment).to eq(last_server_specified_environment)
1312
+ end
1279
1313
 
1280
- it "uses the environment from Puppet[:environment] if given a catalog" do
1281
- configurer.run(catalog: catalog)
1314
+ it "uses the environment from Puppet[:environment] if given a catalog" do
1315
+ configurer.run(catalog: catalog)
1282
1316
 
1283
- expect(configurer.environment).to eq(Puppet[:environment])
1284
- end
1317
+ expect(configurer.environment).to eq(Puppet[:environment])
1318
+ end
1285
1319
 
1286
- it "uses the environment from Puppet[:environment] if use_cached_catalog = true" do
1287
- Puppet[:use_cached_catalog] = true
1288
- expects_cached_catalog_only(catalog)
1289
- configurer.run
1320
+ it "uses the environment from Puppet[:environment] if use_cached_catalog = true" do
1321
+ Puppet[:use_cached_catalog] = true
1322
+ expects_cached_catalog_only(catalog)
1323
+ configurer.run
1290
1324
 
1291
- expect(configurer.environment).to eq(Puppet[:environment])
1292
- end
1325
+ expect(configurer.environment).to eq(Puppet[:environment])
1326
+ end
1293
1327
 
1294
- describe "when the environment is not set via CLI" do
1295
- it "uses the environment found in lastrunfile if the key exists" do
1296
- configurer.run
1328
+ describe "when the environment is not set via CLI" do
1329
+ it "uses the environment found in lastrunfile if the key exists" do
1330
+ configurer.run
1297
1331
 
1298
- expect(configurer.environment).to eq(last_server_specified_environment)
1332
+ expect(configurer.environment).to eq(last_server_specified_environment)
1333
+ end
1334
+
1335
+ it "pushes the converged environment found in lastrunfile over the existing context" do
1336
+ initial_env = Puppet::Node::Environment.remote('production')
1337
+ Puppet.push_context(
1338
+ current_environment: initial_env,
1339
+ loaders: Puppet::Pops::Loaders.new(initial_env, true))
1340
+
1341
+ expect(Puppet).to receive(:push_context).with(
1342
+ hash_including(:current_environment, :loaders),
1343
+ "Local node environment #{last_server_specified_environment} for configurer transaction"
1344
+ ).once.and_call_original
1345
+
1346
+ configurer.run
1347
+ end
1348
+
1349
+ it "uses the environment from Puppet[:environment] if strict_environment_mode is set" do
1350
+ Puppet[:strict_environment_mode] = true
1351
+ configurer.run
1352
+
1353
+ expect(configurer.environment).to eq(Puppet[:environment])
1354
+ end
1355
+
1356
+ it "uses the environment from Puppet[:environment] if initial_environment is the same as converged_environment" do
1357
+ Puppet[:lastrunfile] = file_containing('last_run_summary.yaml', <<~SUMMARY)
1358
+ ---
1359
+ version:
1360
+ config: 1624882680
1361
+ puppet: 6.24.0
1362
+ application:
1363
+ initial_environment: development
1364
+ converged_environment: development
1365
+ run_mode: agent
1366
+ SUMMARY
1367
+ configurer.run
1368
+
1369
+ expect(configurer.environment).to eq(Puppet[:environment])
1370
+ end
1299
1371
  end
1372
+ end
1300
1373
 
1301
- it "pushes the converged environment found in lastrunfile over the existing context" do
1302
- initial_env = Puppet::Node::Environment.remote('production')
1303
- Puppet.push_context(
1304
- current_environment: initial_env,
1305
- loaders: Puppet::Pops::Loaders.new(initial_env, true))
1374
+ describe "when the use_last_environment setting is set to false" do
1375
+ let(:node_environment) { Puppet::Node::Environment.remote(:salam) }
1376
+ let(:node) { Puppet::Node.new(Puppet[:node_name_value]) }
1306
1377
 
1307
- expect(Puppet).to receive(:push_context).with(
1308
- hash_including(:current_environment, :loaders),
1309
- "Local node environment #{last_server_specified_environment} for configurer transaction"
1310
- ).once.and_call_original
1378
+ before do
1379
+ Puppet[:use_last_environment] = false
1380
+ node.environment = node_environment
1311
1381
 
1312
- configurer.run
1382
+ allow(Puppet::Node.indirection).to receive(:find)
1383
+ allow(Puppet::Node.indirection).to receive(:find)
1384
+ .with(anything, hash_including(:ignore_cache => true, :fail_on_404 => true))
1385
+ .and_return(node)
1313
1386
  end
1314
1387
 
1315
- it "uses the environment from Puppet[:environment] if strict_environment_mode is set" do
1316
- Puppet[:strict_environment_mode] = true
1317
- configurer.run
1388
+ it "does a node request" do
1389
+ expect(Puppet::Node.indirection).to receive(:find)
1390
+ .with(anything, hash_including(:ignore_cache => true, :fail_on_404 => true))
1318
1391
 
1319
- expect(configurer.environment).to eq(Puppet[:environment])
1392
+ configurer.run
1320
1393
  end
1321
1394
 
1322
- it "uses the environment from Puppet[:environment] if initial_environment is the same as converged_environment" do
1323
- Puppet[:lastrunfile] = file_containing('last_run_summary.yaml', <<~SUMMARY)
1324
- ---
1325
- version:
1326
- config: 1624882680
1327
- puppet: 6.24.0
1328
- application:
1329
- initial_environment: development
1330
- converged_environment: development
1331
- run_mode: agent
1332
- SUMMARY
1395
+ it "uses the node environment from the node request" do
1333
1396
  configurer.run
1334
1397
 
1335
- expect(configurer.environment).to eq(Puppet[:environment])
1398
+ expect(configurer.environment).to eq(node_environment.name.to_s)
1336
1399
  end
1337
1400
  end
1338
1401
  end
@@ -3,6 +3,8 @@ require 'spec_helper'
3
3
  require 'puppet/confiner'
4
4
 
5
5
  describe Puppet::Confiner do
6
+ let(:coll) { Puppet::ConfineCollection.new('') }
7
+
6
8
  before do
7
9
  @object = Object.new
8
10
  @object.extend(Puppet::Confiner)
@@ -21,7 +23,6 @@ describe Puppet::Confiner do
21
23
  end
22
24
 
23
25
  it "should delegate its confine method to its confine collection" do
24
- coll = double('collection')
25
26
  allow(@object).to receive(:confine_collection).and_return(coll)
26
27
  expect(coll).to receive(:confine).with(:foo => :bar, :bee => :baz)
27
28
  @object.confine(:foo => :bar, :bee => :baz)
@@ -39,22 +40,21 @@ describe Puppet::Confiner do
39
40
 
40
41
  describe "when testing suitability" do
41
42
  before do
42
- @coll = double('collection')
43
- allow(@object).to receive(:confine_collection).and_return(@coll)
43
+ allow(@object).to receive(:confine_collection).and_return(coll)
44
44
  end
45
45
 
46
46
  it "should return true if the confine collection is valid" do
47
- expect(@coll).to receive(:valid?).and_return(true)
47
+ expect(coll).to receive(:valid?).and_return(true)
48
48
  expect(@object).to be_suitable
49
49
  end
50
50
 
51
51
  it "should return false if the confine collection is invalid" do
52
- expect(@coll).to receive(:valid?).and_return(false)
52
+ expect(coll).to receive(:valid?).and_return(false)
53
53
  expect(@object).not_to be_suitable
54
54
  end
55
55
 
56
56
  it "should return the summary of the confine collection if a long result is asked for" do
57
- expect(@coll).to receive(:summary).and_return("myresult")
57
+ expect(coll).to receive(:summary).and_return("myresult")
58
58
  expect(@object.suitable?(false)).to eq("myresult")
59
59
  end
60
60
  end
@@ -221,6 +221,70 @@ describe Puppet::Face[:generate, :current] do
221
221
  end
222
222
 
223
223
  end
224
+
225
+ context "in an environment with a faulty type" do
226
+ let(:dir) do
227
+ dir_containing('environments', { 'testing_generate2' => {
228
+ 'environment.conf' => "modulepath = modules",
229
+ 'manifests' => { 'site.pp' => "" },
230
+ 'modules' => {
231
+ 'm3' => {
232
+ 'lib' => { 'puppet' => { 'type' => {
233
+ 'test3.rb' => <<-EOF
234
+ module Puppet
235
+ Type.newtype(:test3) do
236
+ @doc = "Docs for resource"
237
+ def self.title_patterns
238
+ identity = lambda {|x| x}
239
+ [
240
+ [
241
+ /^(.*)_(.*)$/,
242
+ [
243
+ [:name, identity ]
244
+ ]
245
+ ]
246
+ ]
247
+ end
248
+ newproperty(:message) do
249
+ desc "Docs for 'message' property"
250
+ end
251
+ newparam(:name) do
252
+ desc "Docs for 'name' parameter"
253
+ isnamevar
254
+ end
255
+ end; end
256
+ EOF
257
+ } }
258
+ }
259
+ }
260
+ }}})
261
+ end
262
+
263
+ let(:modulepath) do
264
+ File.join(dir, 'testing_generate2', 'modules')
265
+ end
266
+
267
+ let(:m3) do
268
+ File.join(modulepath, 'm3')
269
+ end
270
+
271
+ around(:each) do |example|
272
+ Puppet.settings.initialize_global_settings
273
+ Puppet[:manifest] = ''
274
+ loader = Puppet::Environments::Directories.new(dir, [])
275
+ Puppet.override(:environments => loader) do
276
+ Puppet.override(:current_environment => loader.get('testing_generate2')) do
277
+ example.run
278
+ end
279
+ end
280
+ end
281
+
282
+ it 'fails when using procs for title patterns' do
283
+ expect {
284
+ genface.types(:format => 'pcore')
285
+ }.to exit_with(1)
286
+ end
287
+ end
224
288
  end
225
289
 
226
290
  def from_an_interactive_terminal
@@ -40,6 +40,12 @@ describe Puppet::Node do
40
40
  expect(node.environment.name).to eq(:bar)
41
41
  end
42
42
 
43
+ it "sets environment_name with the correct environment name" do
44
+ node = Puppet::Node.new("foo")
45
+ node.environment = Puppet::Node::Environment.remote('www123')
46
+ expect(node.environment_name).to eq(:www123)
47
+ end
48
+
43
49
  it "allows its environment to be set by parameters after initialization" do
44
50
  node = Puppet::Node.new("foo")
45
51
  node.parameters["environment"] = :bar
@@ -289,6 +289,73 @@ describe Puppet::Type.type(:user) do
289
289
  end
290
290
  end
291
291
 
292
+ describe "when managing the purge_ssh_keys property" do
293
+ context "with valid input" do
294
+ ['true', :true, true].each do |input|
295
+ it "should support #{input} as value" do
296
+ expect { described_class.new(:name => 'foo', :purge_ssh_keys => input) }.to_not raise_error
297
+ end
298
+ end
299
+
300
+ ['false', :false, false].each do |input|
301
+ it "should support #{input} as value" do
302
+ expect { described_class.new(:name => 'foo', :purge_ssh_keys => input) }.to_not raise_error
303
+ end
304
+ end
305
+
306
+ it "should support a String value" do
307
+ expect { described_class.new(:name => 'foo', :purge_ssh_keys => File.expand_path('home/foo/.ssh/authorized_keys')) }.to_not raise_error
308
+ end
309
+
310
+ it "should support an Array value" do
311
+ expect { described_class.new(:name => 'foo', :purge_ssh_keys => [File.expand_path('home/foo/.ssh/authorized_keys'),
312
+ File.expand_path('custom/authorized_keys')]) }.to_not raise_error
313
+ end
314
+ end
315
+
316
+ context "with faulty input" do
317
+ it "should raise error for relative path" do
318
+ expect { described_class.new(:name => 'foo', :purge_ssh_keys => 'home/foo/.ssh/authorized_keys') }.to raise_error(Puppet::ResourceError,
319
+ /Paths to keyfiles must be absolute/ )
320
+ end
321
+
322
+ it "should raise error for invalid type" do
323
+ expect { described_class.new(:name => 'foo', :purge_ssh_keys => :invalid) }.to raise_error(Puppet::ResourceError,
324
+ /purge_ssh_keys must be true, false, or an array of file names/ )
325
+ end
326
+
327
+ it "should raise error for array with relative path" do
328
+ expect { described_class.new(:name => 'foo', :purge_ssh_keys => ['home/foo/.ssh/authorized_keys',
329
+ File.expand_path('custom/authorized_keys')]) }.to raise_error(Puppet::ResourceError,
330
+ /Paths to keyfiles must be absolute/ )
331
+ end
332
+
333
+ it "should raise error for array with invalid type" do
334
+ expect { described_class.new(:name => 'foo', :purge_ssh_keys => [:invalid,
335
+ File.expand_path('custom/authorized_keys')]) }.to raise_error(Puppet::ResourceError,
336
+ /Each entry for purge_ssh_keys must be a string/ )
337
+ end
338
+ end
339
+
340
+ context "homedir retrieval" do
341
+ it "should accept the home provided" do
342
+ expect(Puppet).not_to receive(:debug).with("User 'foo' does not exist")
343
+ described_class.new(:name => 'foo', :purge_ssh_keys => true, :home => '/my_home')
344
+ end
345
+
346
+ it "should accept the home provided" do
347
+ expect(Dir).to receive(:home).with('foo').and_return('/my_home')
348
+ expect(Puppet).not_to receive(:debug).with("User 'foo' does not exist")
349
+ described_class.new(:name => 'foo', :purge_ssh_keys => true)
350
+ end
351
+
352
+ it "should output debug message when home directory cannot be retrieved" do
353
+ allow(Dir).to receive(:home).with('foo').and_raise(ArgumentError)
354
+ expect(Puppet).to receive(:debug).with("User 'foo' does not exist")
355
+ described_class.new(:name => 'foo', :purge_ssh_keys => true)
356
+ end
357
+ end
358
+ end
292
359
 
293
360
  describe "when managing expiry" do
294
361
  it "should fail if given an invalid date" do
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Puppet::Util::Windows do
6
+ %w[
7
+ ADSI
8
+ ADSI::ADSIObject
9
+ ADSI::User
10
+ ADSI::UserProfile
11
+ ADSI::Group
12
+ EventLog
13
+ File
14
+ Process
15
+ Registry
16
+ Service
17
+ SID
18
+ ].each do |name|
19
+ it "defines Puppet::Util::Windows::#{name}" do
20
+ expect(described_class.const_get(name)).to be
21
+ end
22
+ end
23
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: puppet
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.13.1
4
+ version: 7.16.0
5
5
  platform: universal-darwin
6
6
  authors:
7
7
  - Puppet Labs
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-12-09 00:00:00.000000000 Z
11
+ date: 2022-04-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: facter
@@ -2512,6 +2512,7 @@ files:
2512
2512
  - spec/unit/util/windows/service_spec.rb
2513
2513
  - spec/unit/util/windows/sid_spec.rb
2514
2514
  - spec/unit/util/windows/string_spec.rb
2515
+ - spec/unit/util/windows_spec.rb
2515
2516
  - spec/unit/util/yaml_spec.rb
2516
2517
  - spec/unit/util_spec.rb
2517
2518
  - spec/unit/version_spec.rb
@@ -2551,7 +2552,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
2551
2552
  - !ruby/object:Gem::Version
2552
2553
  version: 1.3.1
2553
2554
  requirements: []
2554
- rubygems_version: 3.0.8
2555
+ rubygems_version: 3.1.6
2555
2556
  signing_key:
2556
2557
  specification_version: 4
2557
2558
  summary: Puppet, an automated configuration management tool
@@ -3771,6 +3772,7 @@ test_files:
3771
3772
  - spec/unit/util/windows/service_spec.rb
3772
3773
  - spec/unit/util/windows/sid_spec.rb
3773
3774
  - spec/unit/util/windows/string_spec.rb
3775
+ - spec/unit/util/windows_spec.rb
3774
3776
  - spec/unit/util/yaml_spec.rb
3775
3777
  - spec/unit/util_spec.rb
3776
3778
  - spec/unit/version_spec.rb