r10k 3.5.1 → 3.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. checksums.yaml +4 -4
  2. data/.github/pull_request_template.md +4 -1
  3. data/.github/workflows/docker.yml +4 -1
  4. data/.github/workflows/release.yml +3 -2
  5. data/.github/workflows/rspec_tests.yml +81 -0
  6. data/.travis.yml +8 -1
  7. data/CHANGELOG.mkd +43 -2
  8. data/CODEOWNERS +2 -2
  9. data/README.mkd +13 -4
  10. data/doc/common-patterns.mkd +1 -0
  11. data/doc/dynamic-environments/configuration.mkd +143 -39
  12. data/doc/dynamic-environments/usage.mkd +12 -11
  13. data/doc/puppetfile.mkd +23 -3
  14. data/docker/Gemfile +1 -1
  15. data/docker/Makefile +7 -4
  16. data/docker/docker-compose.yml +18 -0
  17. data/docker/r10k/Dockerfile +4 -3
  18. data/docker/r10k/docker-entrypoint.sh +0 -1
  19. data/docker/r10k/release.Dockerfile +3 -2
  20. data/docker/spec/dockerfile_spec.rb +26 -32
  21. data/integration/tests/git_source/git_source_repeated_remote.rb +68 -0
  22. data/integration/tests/user_scenario/basic_workflow/multi_env_custom_forge_git_module.rb +2 -1
  23. data/integration/tests/user_scenario/basic_workflow/multi_env_custom_forge_git_module_static.rb +2 -1
  24. data/integration/tests/user_scenario/basic_workflow/multi_source_custom_forge_git_module.rb +1 -1
  25. data/integration/tests/user_scenario/basic_workflow/single_env_custom_forge_git_module.rb +2 -1
  26. data/integration/tests/user_scenario/complex_workflow/multi_env_add_change_remove.rb +1 -1
  27. data/integration/tests/user_scenario/complex_workflow/multi_env_remove_re-add.rb +1 -1
  28. data/integration/tests/user_scenario/complex_workflow/multi_env_unamanaged.rb +1 -1
  29. data/lib/r10k/action/deploy/display.rb +9 -3
  30. data/lib/r10k/action/deploy/environment.rb +39 -14
  31. data/lib/r10k/action/deploy/module.rb +4 -1
  32. data/lib/r10k/action/runner.rb +34 -0
  33. data/lib/r10k/cli/deploy.rb +14 -7
  34. data/lib/r10k/cli/puppetfile.rb +5 -5
  35. data/lib/r10k/environment/base.rb +9 -2
  36. data/lib/r10k/environment/git.rb +17 -2
  37. data/lib/r10k/environment/name.rb +22 -4
  38. data/lib/r10k/environment/svn.rb +11 -2
  39. data/lib/r10k/environment/with_modules.rb +28 -20
  40. data/lib/r10k/forge/module_release.rb +2 -2
  41. data/lib/r10k/git.rb +1 -0
  42. data/lib/r10k/git/cache.rb +12 -4
  43. data/lib/r10k/git/rugged/credentials.rb +39 -2
  44. data/lib/r10k/git/stateful_repository.rb +4 -0
  45. data/lib/r10k/initializers.rb +2 -0
  46. data/lib/r10k/module/base.rb +8 -0
  47. data/lib/r10k/module/forge.rb +16 -4
  48. data/lib/r10k/module/git.rb +42 -24
  49. data/lib/r10k/module/local.rb +1 -1
  50. data/lib/r10k/module/svn.rb +14 -11
  51. data/lib/r10k/puppetfile.rb +30 -12
  52. data/lib/r10k/settings.rb +30 -3
  53. data/lib/r10k/source/base.rb +5 -0
  54. data/lib/r10k/source/git.rb +26 -3
  55. data/lib/r10k/source/hash.rb +4 -2
  56. data/lib/r10k/source/svn.rb +5 -1
  57. data/lib/r10k/util/setopts.rb +33 -12
  58. data/lib/r10k/version.rb +1 -1
  59. data/locales/r10k.pot +71 -43
  60. data/r10k.gemspec +1 -1
  61. data/spec/fixtures/unit/action/r10k_creds.yaml +9 -0
  62. data/spec/shared-examples/subprocess-runner.rb +11 -5
  63. data/spec/unit/action/deploy/display_spec.rb +4 -0
  64. data/spec/unit/action/deploy/environment_spec.rb +154 -12
  65. data/spec/unit/action/deploy/module_spec.rb +40 -1
  66. data/spec/unit/action/puppetfile/install_spec.rb +1 -0
  67. data/spec/unit/action/runner_spec.rb +48 -1
  68. data/spec/unit/environment/git_spec.rb +19 -2
  69. data/spec/unit/environment/name_spec.rb +28 -0
  70. data/spec/unit/environment/svn_spec.rb +12 -0
  71. data/spec/unit/environment/with_modules_spec.rb +74 -0
  72. data/spec/unit/forge/module_release_spec.rb +14 -10
  73. data/spec/unit/git/cache_spec.rb +10 -0
  74. data/spec/unit/git/rugged/credentials_spec.rb +79 -2
  75. data/spec/unit/git_spec.rb +3 -3
  76. data/spec/unit/module/forge_spec.rb +6 -0
  77. data/spec/unit/module/git_spec.rb +56 -1
  78. data/spec/unit/module_spec.rb +59 -9
  79. data/spec/unit/puppetfile_spec.rb +61 -7
  80. data/spec/unit/settings_spec.rb +12 -0
  81. data/spec/unit/source/git_spec.rb +49 -1
  82. data/spec/unit/util/setopts_spec.rb +25 -1
  83. metadata +9 -11
  84. data/azure-pipelines.yml +0 -86
@@ -15,6 +15,22 @@ describe R10K::Environment::Git do
15
15
  )
16
16
  end
17
17
 
18
+ describe "initializing" do
19
+ subject do
20
+ described_class.new('name', '/dir', 'ref', {
21
+ :remote => 'url',
22
+ :ref => 'value',
23
+ :puppetfile_name => 'Puppetfile',
24
+ :moduledir => 'modules',
25
+ :modules => { },
26
+ })
27
+ end
28
+
29
+ it "accepts valid base class initialization arguments" do
30
+ expect(subject.name).to eq 'name'
31
+ end
32
+ end
33
+
18
34
  describe "storing attributes" do
19
35
  it "can return the environment name" do
20
36
  expect(subject.name).to eq 'myenv'
@@ -62,9 +78,10 @@ describe R10K::Environment::Git do
62
78
 
63
79
  describe "enumerating modules" do
64
80
  it "loads the Puppetfile and returns modules in that puppetfile" do
81
+ mod = double('A module', :name => 'dbl')
65
82
  expect(subject.puppetfile).to receive(:load)
66
- expect(subject.puppetfile).to receive(:modules).and_return [:modules]
67
- expect(subject.modules).to eq([:modules])
83
+ expect(subject.puppetfile).to receive(:modules).and_return [mod]
84
+ expect(subject.modules).to eq([mod])
68
85
  end
69
86
  end
70
87
 
@@ -2,6 +2,34 @@ require 'spec_helper'
2
2
  require 'r10k/environment/name'
3
3
 
4
4
  describe R10K::Environment::Name do
5
+ describe "strip_component" do
6
+ it "does not modify the given name when no strip_component is given" do
7
+ bn = described_class.new('myenv', source: 'source', prefix: false)
8
+ expect(bn.dirname).to eq 'myenv'
9
+ end
10
+
11
+ it "removes the first occurance of a regex match when a regex is given" do
12
+ bn = described_class.new('myenv', source: 'source', prefix: false, strip_component: '/env/')
13
+ expect(bn.dirname).to eq 'my'
14
+ end
15
+
16
+ it "does not modify the given name when there is no regex match" do
17
+ bn = described_class.new('myenv', source: 'source', prefix: false, strip_component: '/bar/')
18
+ expect(bn.dirname).to eq 'myenv'
19
+ end
20
+
21
+ it "removes the given name's prefix when it matches strip_component" do
22
+ bn = described_class.new('env/prod', source: 'source', prefix: false, strip_component: 'env/')
23
+ expect(bn.dirname).to eq 'prod'
24
+ end
25
+
26
+ it "raises an error when given an integer" do
27
+ expect {
28
+ described_class.new('env/prod', source: 'source', prefix: false, strip_component: 4)
29
+ }.to raise_error(%r{Improper.*"4"})
30
+ end
31
+ end
32
+
5
33
  describe "prefixing" do
6
34
  it "uses the branch name as the dirname when prefixing is off" do
7
35
  bn = described_class.new('mybranch', :source => 'source', :prefix => false)
@@ -16,6 +16,18 @@ describe R10K::Environment::SVN do
16
16
 
17
17
  let(:working_dir) { subject.working_dir }
18
18
 
19
+ describe "initializing" do
20
+ subject do
21
+ described_class.new('name', '/dir', 'ref', {
22
+ :puppetfile_name => 'Puppetfile',
23
+ })
24
+ end
25
+
26
+ it "accepts valid base class initialization arguments" do
27
+ expect(subject.name).to eq 'name'
28
+ end
29
+ end
30
+
19
31
  describe "storing attributes" do
20
32
  it "can return the environment name" do
21
33
  expect(subject.name).to eq 'myenv'
@@ -0,0 +1,74 @@
1
+ require 'spec_helper'
2
+ require 'r10k/environment'
3
+
4
+ describe R10K::Environment::WithModules do
5
+ subject do
6
+ described_class.new(
7
+ 'release42',
8
+ '/some/nonexistent/environmentdir',
9
+ 'prefix_release42',
10
+ {
11
+ :type => 'bare',
12
+ :modules => {
13
+ 'puppetlabs-stdlib' => { local: true },
14
+ 'puppetlabs-concat' => { local: true },
15
+ 'puppetlabs-exec' => { local: true },
16
+ }
17
+ }.merge(subject_params)
18
+ )
19
+ end
20
+
21
+ # Default no additional params
22
+ let(:subject_params) { {} }
23
+
24
+ describe "dealing with module conflicts" do
25
+ context "with no module conflicts" do
26
+ it "validates when there are no conflicts" do
27
+ mod = instance_double('R10K::Module::Base', name: 'nonconflict', origin: :puppetfile)
28
+ expect(subject.module_conflicts?(mod)).to eq false
29
+ end
30
+ end
31
+
32
+ context "with module conflicts and default behavior" do
33
+ it "does not raise an error" do
34
+ mod = instance_double('R10K::Module::Base', name: 'stdlib', origin: :puppetfile)
35
+ expect(subject.logger).to receive(:warn).with(/Puppetfile.*both define.*ignored/i)
36
+ expect(subject.module_conflicts?(mod)).to eq true
37
+ end
38
+ end
39
+
40
+ context "with module conflicts and 'error' behavior" do
41
+ let(:subject_params) {{ :module_conflicts => 'error' }}
42
+ it "raises an error" do
43
+ mod = instance_double('R10K::Module::Base', name: 'stdlib', origin: :puppetfile)
44
+ expect { subject.module_conflicts?(mod) }.to raise_error(R10K::Error, /Puppetfile.*both define.*/i)
45
+ end
46
+ end
47
+
48
+ context "with module conflicts and 'override' behavior" do
49
+ let(:subject_params) {{ :module_conflicts => 'override' }}
50
+ it "does not raise an error" do
51
+ mod = instance_double('R10K::Module::Base', name: 'stdlib', origin: :puppetfile)
52
+ expect(subject.logger).to receive(:debug).with(/Puppetfile.*both define.*ignored/i)
53
+ expect(subject.module_conflicts?(mod)).to eq true
54
+ end
55
+ end
56
+
57
+ context "with module conflicts and invalid configuration" do
58
+ let(:subject_params) {{ :module_conflicts => 'batman' }}
59
+ it "raises an error" do
60
+ mod = instance_double('R10K::Module::Base', name: 'stdlib', origin: :puppetfile)
61
+ expect { subject.module_conflicts?(mod) }.to raise_error(R10K::Error, /Unexpected value.*module_conflicts.*/i)
62
+ end
63
+ end
64
+ end
65
+
66
+ describe "modules method" do
67
+ it "returns the configured modules, and Puppetfile modules" do
68
+ puppetfile_mod = instance_double('R10K::Module::Base', name: 'zebra')
69
+ expect(subject.puppetfile).to receive(:modules).and_return [puppetfile_mod]
70
+ returned_modules = subject.modules
71
+ expect(returned_modules.map(&:name).sort).to eq(%w[concat exec stdlib zebra])
72
+ end
73
+ end
74
+ end
@@ -166,33 +166,37 @@ describe R10K::Forge::ModuleRelease do
166
166
  end
167
167
 
168
168
  describe "#cleanup_unpack_path" do
169
- it "ignores the unpack_path if it doesn't exist" do
170
- expect(unpack_path).to receive(:exist?).and_return false
171
- expect(unpack_path).to_not receive(:parent)
169
+ it "ignores the unpack_path if the parent doesn't exist" do
170
+ parent = instance_double('Pathname')
171
+ expect(parent).to receive(:exist?).and_return false
172
+ expect(parent).to_not receive(:rmtree)
173
+ expect(unpack_path).to receive(:parent).and_return(parent)
172
174
  subject.cleanup_unpack_path
173
175
  end
174
176
 
175
177
  it "removes the containing directory of unpack_path if it exists" do
176
178
  parent = instance_double('Pathname')
177
179
  expect(parent).to receive(:rmtree)
178
- expect(unpack_path).to receive(:exist?).and_return true
179
- expect(unpack_path).to receive(:parent).and_return(parent)
180
+ expect(parent).to receive(:exist?).and_return true
181
+ expect(unpack_path).to receive(:parent).and_return(parent).exactly(2).times
180
182
  subject.cleanup_unpack_path
181
183
  end
182
184
  end
183
185
 
184
186
  describe "#cleanup_download_path" do
185
- it "ignores the download_path if it doesn't exist" do
186
- expect(download_path).to receive(:exist?).and_return false
187
- expect(download_path).to_not receive(:parent)
187
+ it "ignores the download_path if the parent doesn't exist" do
188
+ parent = instance_double('Pathname')
189
+ expect(parent).to receive(:exist?).and_return false
190
+ expect(parent).to_not receive(:rmtree)
191
+ expect(download_path).to receive(:parent).and_return(parent)
188
192
  subject.cleanup_download_path
189
193
  end
190
194
 
191
195
  it "removes the containing directory of download_path if it exists" do
192
196
  parent = instance_double('Pathname')
193
197
  expect(parent).to receive(:rmtree)
194
- expect(download_path).to receive(:exist?).and_return true
195
- expect(download_path).to receive(:parent).and_return(parent)
198
+ expect(parent).to receive(:exist?).and_return true
199
+ expect(download_path).to receive(:parent).and_return(parent).exactly(2).times
196
200
  subject.cleanup_download_path
197
201
  end
198
202
  end
@@ -3,6 +3,16 @@ require 'r10k/git/cache'
3
3
 
4
4
  describe R10K::Git::Cache do
5
5
 
6
+ describe 'the default cache_root' do
7
+ it 'is in the right location in linux', unless: R10K::Util::Platform.windows? do
8
+ expect(described_class.defaults[:cache_root]).to match(/\.r10k\/git/)
9
+ end
10
+
11
+ it 'is in the right location for windows', if: R10K::Util::Platform.windows? do
12
+ expect(described_class.defaults[:cache_root]).to match(/[^.]r10k\/git/)
13
+ end
14
+ end
15
+
6
16
  let(:subclass) do
7
17
  Class.new(described_class) do
8
18
  def self.bare_repository
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe R10K::Git::Rugged::Credentials, :unless => R10K::Util::Platform.jruby? do
3
+ describe R10K::Git::Rugged::Credentials, :unless => R10K::Util::Platform.jruby? || R10K::Util::Platform.windows? do
4
4
  before(:all) do
5
5
  require 'r10k/git/rugged/credentials'
6
6
  require 'rugged/credentials'
@@ -10,7 +10,7 @@ describe R10K::Git::Rugged::Credentials, :unless => R10K::Util::Platform.jruby?
10
10
 
11
11
  subject { described_class.new(repo) }
12
12
 
13
- after(:all) { R10K::Git.settings.reset! }
13
+ after(:each) { R10K::Git.settings.reset! }
14
14
 
15
15
  describe "determining the username" do
16
16
  before { R10K::Git.settings[:username] = "moderns" }
@@ -39,6 +39,7 @@ describe R10K::Git::Rugged::Credentials, :unless => R10K::Util::Platform.jruby?
39
39
 
40
40
  it "prefers a per-repository SSH private key" do
41
41
  allow(File).to receive(:readable?).with("/etc/puppetlabs/r10k/ssh/tessier-ashpool-id_rsa").and_return true
42
+ R10K::Git.settings[:private_key] = "/etc/puppetlabs/r10k/ssh/id_rsa"
42
43
  R10K::Git.settings[:repositories] = [{ remote: "ssh://git@tessier-ashpool.freeside/repo.git",
43
44
  private_key: "/etc/puppetlabs/r10k/ssh/tessier-ashpool-id_rsa"}]
44
45
  creds = subject.get_ssh_key_credentials("ssh://git@tessier-ashpool.freeside/repo.git", nil)
@@ -78,6 +79,82 @@ describe R10K::Git::Rugged::Credentials, :unless => R10K::Util::Platform.jruby?
78
79
  end
79
80
  end
80
81
 
82
+ describe "generating token credentials" do
83
+ it 'errors if token file does not exist' do
84
+ R10K::Git.settings[:oauth_token] = "/missing/token/file"
85
+ expect(File).to receive(:readable?).with("/missing/token/file").and_return false
86
+ R10K::Git.settings[:repositories] = [{remote: "https://tessier-ashpool.freeside/repo.git"}]
87
+ expect {
88
+ subject.get_plaintext_credentials("https://tessier-ashpool.freeside/repo.git", nil)
89
+ }.to raise_error(R10K::Git::GitError, /cannot load OAuth token/)
90
+ end
91
+
92
+ it 'errors if the token on stdin is not a valid OAuth token' do
93
+ allow($stdin).to receive(:read).and_return("<bad>token")
94
+ R10K::Git.settings[:oauth_token] = "-"
95
+ R10K::Git.settings[:repositories] = [{remote: "https://tessier-ashpool.freeside/repo.git"}]
96
+ expect {
97
+ subject.get_plaintext_credentials("https://tessier-ashpool.freeside/repo.git", nil)
98
+ }.to raise_error(R10K::Git::GitError, /invalid characters/)
99
+ end
100
+
101
+ it 'errors if the token in the file is not a valid OAuth token' do
102
+ token_file = Tempfile.new('token')
103
+ token_file.write('my bad \ntoken')
104
+ token_file.close
105
+ R10K::Git.settings[:oauth_token] = token_file.path
106
+ R10K::Git.settings[:repositories] = [{remote: "https://tessier-ashpool.freeside/repo.git"}]
107
+ expect {
108
+ subject.get_plaintext_credentials("https://tessier-ashpool.freeside/repo.git", nil)
109
+ }.to raise_error(R10K::Git::GitError, /invalid characters/)
110
+ end
111
+
112
+ it 'prefers per-repo token file' do
113
+ token_file = Tempfile.new('token')
114
+ token_file.write('my_token')
115
+ token_file.close
116
+ R10K::Git.settings[:oauth_token] = "/do/not/use"
117
+ R10K::Git.settings[:repositories] = [{remote: "https://tessier-ashpool.freeside/repo.git",
118
+ oauth_token: token_file.path }]
119
+ creds = subject.get_plaintext_credentials("https://tessier-ashpool.freeside/repo.git", nil)
120
+ expect(creds).to be_a_kind_of(Rugged::Credentials::UserPassword)
121
+ expect(creds.instance_variable_get(:@password)).to eq("my_token")
122
+ expect(creds.instance_variable_get(:@username)).to eq("x-oauth-token")
123
+ end
124
+
125
+ it 'uses the token from a file as a password' do
126
+ token_file = Tempfile.new('token')
127
+ token_file.write('my_token')
128
+ token_file.close
129
+ R10K::Git.settings[:oauth_token] = token_file.path
130
+ R10K::Git.settings[:repositories] = [{remote: "https://tessier-ashpool.freeside/repo.git"}]
131
+ creds = subject.get_plaintext_credentials("https://tessier-ashpool.freeside/repo.git", nil)
132
+ expect(creds).to be_a_kind_of(Rugged::Credentials::UserPassword)
133
+ expect(creds.instance_variable_get(:@password)).to eq("my_token")
134
+ expect(creds.instance_variable_get(:@username)).to eq("x-oauth-token")
135
+ end
136
+
137
+ it 'uses the token from stdin as a password' do
138
+ allow($stdin).to receive(:read).and_return("my_token")
139
+ R10K::Git.settings[:oauth_token] = '-'
140
+ R10K::Git.settings[:repositories] = [{remote: "https://tessier-ashpool.freeside/repo.git"}]
141
+ creds = subject.get_plaintext_credentials("https://tessier-ashpool.freeside/repo.git", nil)
142
+ expect(creds).to be_a_kind_of(Rugged::Credentials::UserPassword)
143
+ expect(creds.instance_variable_get(:@password)).to eq("my_token")
144
+ expect(creds.instance_variable_get(:@username)).to eq("x-oauth-token")
145
+ end
146
+
147
+ it 'only reads the token in once' do
148
+ expect($stdin).to receive(:read).and_return("my_token").once
149
+ R10K::Git.settings[:oauth_token] = '-'
150
+ R10K::Git.settings[:repositories] = [{remote: "https://tessier-ashpool.freeside/repo.git"}]
151
+ creds = subject.get_plaintext_credentials("https://tessier-ashpool.freeside/repo.git", nil)
152
+ expect(creds.instance_variable_get(:@password)).to eq("my_token")
153
+ creds = subject.get_plaintext_credentials("https://tessier-ashpool.freeside/repo.git", nil)
154
+ expect(creds.instance_variable_get(:@password)).to eq("my_token")
155
+ end
156
+ end
157
+
81
158
  describe "generating default credentials" do
82
159
  it "generates the rugged default credential type" do
83
160
  creds = subject.get_default_credentials("https://azurediamond:hunter2@tessier-ashpool.freeside/repo.git", "azurediamond")
@@ -11,7 +11,7 @@ describe R10K::Git do
11
11
  expect(described_class.default_name).to eq :shellgit
12
12
  end
13
13
 
14
- context 'under c-based rubies', :unless => R10K::Util::Platform.jruby? do
14
+ context 'under c-based rubies with rugged available', :unless => R10K::Util::Platform.jruby? || R10K::Util::Platform.windows? do
15
15
  it 'returns rugged when the git executable is absent and the rugged library is present' do
16
16
  expect(R10K::Features).to receive(:available?).with(:shellgit).and_return false
17
17
  expect(R10K::Features).to receive(:available?).with(:rugged).and_return true
@@ -53,7 +53,7 @@ describe R10K::Git do
53
53
  }.to raise_error(R10K::Error, "Git provider 'shellgit' is not functional.")
54
54
  end
55
55
 
56
- context 'under c-based rubies', :unless => R10K::Util::Platform.jruby? do
56
+ context 'under c-based rubies with rugged available', :unless => R10K::Util::Platform.jruby? || R10K::Util::Platform.windows? do
57
57
  it "sets the current provider if the provider exists and is functional" do
58
58
  expect(R10K::Features).to receive(:available?).with(:rugged).and_return true
59
59
  described_class.provider = :rugged
@@ -71,7 +71,7 @@ describe R10K::Git do
71
71
  end
72
72
 
73
73
  describe "retrieving the current provider" do
74
- context 'under c-based rubies', :unless => R10K::Util::Platform.jruby? do
74
+ context 'under c-based rubies', :unless => R10K::Util::Platform.jruby? || R10K::Util::Platform.windows? do
75
75
  it "uses the default if a provider has not been set" do
76
76
  expect(described_class).to receive(:default_name).and_return :rugged
77
77
  expect(described_class.provider).to eq(R10K::Git::Rugged)
@@ -23,6 +23,12 @@ describe R10K::Module::Forge do
23
23
  end
24
24
  end
25
25
 
26
+ describe "implementing the standard options interface" do
27
+ it "should implement {type: forge}" do
28
+ expect(described_class).to be_implement('branan-eight_hundred', {type: 'forge', version: '8.0.0', source: 'not implemented'})
29
+ end
30
+ end
31
+
26
32
  describe "setting attributes" do
27
33
  subject { described_class.new('branan/eight_hundred', '/moduledir', '8.0.0') }
28
34
 
@@ -123,7 +123,7 @@ describe R10K::Module::Git do
123
123
  let(:opts) { { unrecognized: true } }
124
124
 
125
125
  it "raises an error" do
126
- expect { test_module(opts) }.to raise_error(ArgumentError, /unhandled options.*unrecognized/i)
126
+ expect { test_module(opts) }.to raise_error(ArgumentError, /cannot handle option 'unrecognized'/)
127
127
  end
128
128
  end
129
129
 
@@ -268,6 +268,61 @@ describe R10K::Module::Git do
268
268
  end
269
269
  end
270
270
  end
271
+
272
+ context "when using default_branch_override" do
273
+ before(:each) do
274
+ allow(mock_repo).to receive(:resolve).with(mock_env.ref).and_return(nil)
275
+ end
276
+
277
+ context "and the default branch override is resolvable" do
278
+ it "uses the override" do
279
+ expect(mock_repo).to receive(:resolve).with('default_override').and_return('5566aabb')
280
+ mod = test_module({branch: :control_branch,
281
+ default_branch: 'default',
282
+ default_branch_override: 'default_override'},
283
+ mock_env)
284
+ expect(mod.properties).to include(expected: 'default_override')
285
+ end
286
+ end
287
+
288
+ context "and the default branch override is not resolvable" do
289
+ context "and default branch is provided" do
290
+ it "falls back to the default" do
291
+ expect(mock_repo).to receive(:resolve).with('default_override').and_return(nil)
292
+ expect(mock_repo).to receive(:resolve).with('default').and_return('5566aabb')
293
+ mod = test_module({branch: :control_branch,
294
+ default_branch: 'default',
295
+ default_branch_override: 'default_override'},
296
+ mock_env)
297
+ expect(mod.properties).to include(expected: 'default')
298
+ end
299
+ end
300
+
301
+ context "and default branch is not provided" do
302
+ it "raises the appropriate error" do
303
+ expect(mock_repo).to receive(:resolve).with('default_override').and_return(nil)
304
+ mod = test_module({branch: :control_branch,
305
+ default_branch_override: 'default_override'},
306
+ mock_env)
307
+
308
+ expect { mod.properties }.to raise_error(ArgumentError, /unable to manage.*or resolve the default branch override.*no default provided/i)
309
+ end
310
+ end
311
+
312
+ context "and default branch is not resolvable" do
313
+ it "raises the appropriate error" do
314
+ expect(mock_repo).to receive(:resolve).with('default_override').and_return(nil)
315
+ expect(mock_repo).to receive(:resolve).with('default').and_return(nil)
316
+ mod = test_module({branch: :control_branch,
317
+ default_branch: 'default',
318
+ default_branch_override: 'default_override'},
319
+ mock_env)
320
+
321
+ expect { mod.properties }.to raise_error(ArgumentError, /unable to manage.*or resolve the default branch override.*or resolve default/i)
322
+ end
323
+ end
324
+ end
325
+ end
271
326
  end
272
327
  end
273
328
  end