berkshelf 3.0.0.beta7 → 3.0.0.beta8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +4 -1
  4. data/CONTRIBUTING.md +1 -1
  5. data/Gemfile +0 -1
  6. data/Guardfile +0 -8
  7. data/README.md +33 -13
  8. data/berkshelf.gemspec +3 -3
  9. data/features/commands/install.feature +16 -88
  10. data/features/commands/search.feature +15 -0
  11. data/features/commands/shelf/show.feature +2 -2
  12. data/features/commands/shelf/uninstall.feature +1 -1
  13. data/features/commands/show.feature +3 -3
  14. data/features/commands/update.feature +29 -1
  15. data/features/commands/upload.feature +172 -7
  16. data/features/commands/vendor.feature +32 -0
  17. data/features/json_formatter.feature +26 -24
  18. data/features/lifecycle.feature +285 -0
  19. data/features/lockfile.feature +9 -7
  20. data/features/step_definitions/chef_server_steps.rb +1 -0
  21. data/features/step_definitions/cli_steps.rb +2 -2
  22. data/features/step_definitions/filesystem_steps.rb +2 -4
  23. data/gem_graph.png +0 -0
  24. data/generator_files/chefignore +0 -2
  25. data/lib/berkshelf.rb +39 -14
  26. data/lib/berkshelf/berksfile.rb +161 -113
  27. data/lib/berkshelf/cached_cookbook.rb +2 -2
  28. data/lib/berkshelf/cli.rb +15 -3
  29. data/lib/berkshelf/commands/shelf.rb +3 -7
  30. data/lib/berkshelf/community_rest.rb +9 -9
  31. data/lib/berkshelf/config.rb +3 -3
  32. data/lib/berkshelf/cookbook_generator.rb +0 -8
  33. data/lib/berkshelf/cookbook_store.rb +1 -2
  34. data/lib/berkshelf/dependency.rb +25 -138
  35. data/lib/berkshelf/downloader.rb +41 -7
  36. data/lib/berkshelf/errors.rb +113 -214
  37. data/lib/berkshelf/formatters/base.rb +42 -0
  38. data/lib/berkshelf/formatters/human.rb +145 -0
  39. data/lib/berkshelf/formatters/json.rb +149 -133
  40. data/lib/berkshelf/formatters/null.rb +8 -18
  41. data/lib/berkshelf/init_generator.rb +1 -1
  42. data/lib/berkshelf/installer.rb +115 -104
  43. data/lib/berkshelf/location.rb +22 -121
  44. data/lib/berkshelf/locations/base.rb +75 -0
  45. data/lib/berkshelf/locations/git.rb +196 -0
  46. data/lib/berkshelf/locations/github.rb +8 -0
  47. data/lib/berkshelf/locations/path.rb +78 -0
  48. data/lib/berkshelf/lockfile.rb +452 -290
  49. data/lib/berkshelf/logger.rb +9 -3
  50. data/lib/berkshelf/mixin/logging.rb +4 -9
  51. data/lib/berkshelf/resolver.rb +12 -12
  52. data/lib/berkshelf/source.rb +13 -1
  53. data/lib/berkshelf/version.rb +1 -1
  54. data/spec/fixtures/cookbooks/example_cookbook-0.5.0/metadata.rb +3 -7
  55. data/spec/fixtures/cookbooks/example_cookbook/metadata.rb +3 -6
  56. data/spec/spec_helper.rb +5 -6
  57. data/spec/support/matchers/file_system_matchers.rb +4 -0
  58. data/spec/support/shared_examples/formatter.rb +11 -0
  59. data/spec/unit/berkshelf/berksfile_spec.rb +25 -28
  60. data/spec/unit/berkshelf/cli_spec.rb +19 -11
  61. data/spec/unit/berkshelf/dependency_spec.rb +4 -164
  62. data/spec/unit/berkshelf/formatters/base_spec.rb +35 -0
  63. data/spec/unit/berkshelf/formatters/human_spec.rb +7 -0
  64. data/spec/unit/berkshelf/formatters/json_spec.rb +7 -0
  65. data/spec/unit/berkshelf/formatters/null_spec.rb +7 -11
  66. data/spec/unit/berkshelf/location_spec.rb +16 -144
  67. data/spec/unit/berkshelf/locations/base_spec.rb +80 -0
  68. data/spec/unit/berkshelf/locations/git_spec.rb +249 -0
  69. data/spec/unit/berkshelf/locations/path_spec.rb +107 -0
  70. data/spec/unit/berkshelf/lockfile_parser_spec.rb +3 -3
  71. data/spec/unit/berkshelf/lockfile_spec.rb +55 -11
  72. data/spec/unit/berkshelf/logger_spec.rb +2 -2
  73. data/spec/unit/berkshelf/mixin/logging_spec.rb +5 -9
  74. data/spec/unit/berkshelf/source_spec.rb +32 -13
  75. data/spec/unit/berkshelf_spec.rb +6 -9
  76. metadata +33 -33
  77. data/.ruby-version +0 -1
  78. data/berkshelf-complete.sh +0 -75
  79. data/lib/berkshelf/formatters.rb +0 -110
  80. data/lib/berkshelf/formatters/human_readable.rb +0 -142
  81. data/lib/berkshelf/git.rb +0 -204
  82. data/lib/berkshelf/locations/git_location.rb +0 -135
  83. data/lib/berkshelf/locations/github_location.rb +0 -55
  84. data/lib/berkshelf/locations/mercurial_location.rb +0 -114
  85. data/lib/berkshelf/locations/path_location.rb +0 -88
  86. data/lib/berkshelf/mercurial.rb +0 -146
  87. data/lib/berkshelf/mixin.rb +0 -7
  88. data/spec/support/mercurial.rb +0 -123
  89. data/spec/unit/berkshelf/formatters_spec.rb +0 -114
  90. data/spec/unit/berkshelf/git_spec.rb +0 -312
  91. data/spec/unit/berkshelf/locations/git_location_spec.rb +0 -126
  92. data/spec/unit/berkshelf/locations/mercurial_location_spec.rb +0 -131
  93. data/spec/unit/berkshelf/locations/path_location_spec.rb +0 -25
  94. data/spec/unit/berkshelf/mercurial_spec.rb +0 -172
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+
3
+ module Berkshelf
4
+ describe BaseFormatter do
5
+ it 'has abstract methods for all the messaging modes' do
6
+ expect {
7
+ subject.install('my_coobook','1.2.3','http://community')
8
+ }.to raise_error(AbstractFunction)
9
+
10
+ expect {
11
+ subject.use('my_coobook','1.2.3')
12
+ }.to raise_error(AbstractFunction)
13
+
14
+ expect {
15
+ subject.use('my_coobook','1.2.3','http://community')
16
+ }.to raise_error(AbstractFunction)
17
+
18
+ expect {
19
+ subject.upload('my_coobook','1.2.3','http://chef_server')
20
+ }.to raise_error(AbstractFunction)
21
+
22
+ expect {
23
+ subject.msg('something you to know')
24
+ }.to raise_error(AbstractFunction)
25
+
26
+ expect {
27
+ subject.error('whoa this is bad')
28
+ }.to raise_error(AbstractFunction)
29
+
30
+ expect {
31
+ subject.fetch(double('dependency'))
32
+ }.to raise_error(AbstractFunction)
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ module Berkshelf
4
+ describe HumanFormatter do
5
+ it_behaves_like 'a formatter object'
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ module Berkshelf
4
+ describe JsonFormatter do
5
+ it_behaves_like 'a formatter object'
6
+ end
7
+ end
@@ -1,17 +1,13 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Berkshelf::Formatters::Null do
4
- before { Berkshelf.set_format(:null) }
3
+ module Berkshelf
4
+ describe NullFormatter do
5
+ it_behaves_like 'a formatter object'
5
6
 
6
- [:install, :package, :foo, :bar, :bacon].each do |meth|
7
- it "does not raise an error for :#{meth}" do
8
- expect {
9
- subject.send(meth)
10
- }.to_not raise_error
11
- end
12
-
13
- it "returns nil for :#{meth}" do
14
- expect(subject.send(meth)).to be_nil
7
+ it 'does not raise an error for abstract metods methods' do
8
+ expect { subject.install }.to_not raise_error
9
+ expect { subject.use }.to_not raise_error
10
+ expect { subject.msg }.to_not raise_error
15
11
  end
16
12
  end
17
13
  end
@@ -1,156 +1,28 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Berkshelf::Location do
4
- describe "ClassMethods" do
5
- describe "::init" do
6
- let(:dependency) { double('dependency') }
3
+ module Berkshelf
4
+ describe Location do
5
+ let(:dependency) { double(name: 'bacon') }
7
6
 
8
- it 'returns an instance of PathLocation given a path: option key' do
9
- result = described_class.init(dependency, path: '/Users/reset/code')
10
- expect(result).to be_a(Berkshelf::PathLocation)
7
+ describe '.init' do
8
+ it 'finds a :path location by key' do
9
+ instance = described_class.init(dependency, path: '~/Dev/meats/bacon')
10
+ expect(instance).to be_a(PathLocation)
11
11
  end
12
12
 
13
- it 'returns an instance of GitLocation given a git: option key' do
14
- result = described_class.init(dependency, git: 'git://github.com/something.git')
15
- expect(result).to be_a(Berkshelf::GitLocation)
13
+ it 'finds a :git location by key' do
14
+ instance = described_class.init(dependency, git: 'git://foo.com/meats/bacon.git')
15
+ expect(instance).to be_a(GitLocation)
16
16
  end
17
17
 
18
- context 'given two location_keys' do
19
- it 'raises an InternalError' do
20
- expect {
21
- described_class.init(dependency, git: :value, path: :value)
22
- }.to raise_error(Berkshelf::InternalError)
23
- end
18
+ it 'finds a :github location by key' do
19
+ instance = described_class.init(dependency, github: 'meats/bacon')
20
+ expect(instance).to be_a(GitLocation)
24
21
  end
25
- end
26
- end
27
- end
28
-
29
- describe Berkshelf::Location::Base do
30
- describe "ClassMethods" do
31
- subject { Class.new(described_class) }
32
-
33
- describe "::set_location_key" do
34
- before do
35
- @original = Berkshelf::Dependency.class_variable_get :@@location_keys
36
- Berkshelf::Dependency.class_variable_set :@@location_keys, {}
37
- end
38
-
39
- after do
40
- Berkshelf::Dependency.class_variable_set :@@location_keys, @original
41
- end
42
-
43
- it 'adds the given location key Berkshelf::Dependency.location_keys' do
44
- subject.set_location_key(:reset)
45
-
46
- expect(Berkshelf::Dependency.location_keys).to have(1).item
47
- expect(Berkshelf::Dependency.location_keys).to include(:reset)
48
- expect(Berkshelf::Dependency.location_keys[:reset]).to eq(subject)
49
- end
50
- end
51
-
52
- describe "::location_key" do
53
- before do
54
- @original = Berkshelf::Dependency.class_variable_get :@@location_keys
55
- Berkshelf::Dependency.class_variable_set :@@location_keys, {}
56
- end
57
-
58
- after do
59
- Berkshelf::Dependency.class_variable_set :@@location_keys, @original
60
- end
61
-
62
- it "returns the class' registered location key" do
63
- subject.set_location_key(:reset)
64
- expect(subject.location_key).to eq(:reset)
65
- end
66
- end
67
-
68
- describe "::set_valid_options" do
69
- before do
70
- @original = Berkshelf::Dependency.class_variable_get :@@valid_options
71
- Berkshelf::Dependency.class_variable_set :@@valid_options, []
72
- end
73
-
74
- after do
75
- Berkshelf::Dependency.class_variable_set :@@valid_options, @original
76
- end
77
-
78
- it 'adds the given symbol to the list of valid options on Berkshelf::Dependency' do
79
- subject.set_valid_options(:mundo)
80
-
81
- expect(Berkshelf::Dependency.valid_options).to have(1).item
82
- expect(Berkshelf::Dependency.valid_options).to include(:mundo)
83
- end
84
-
85
- it 'adds parameters to the list of valid options on the Berkshelf::Dependency' do
86
- subject.set_valid_options(:riot, :arenanet)
87
-
88
- expect(Berkshelf::Dependency.valid_options).to have(2).items
89
- expect(Berkshelf::Dependency.valid_options).to include(:riot)
90
- expect(Berkshelf::Dependency.valid_options).to include(:arenanet)
91
- end
92
- end
93
- end
94
-
95
- let(:name) { "nginx" }
96
- let(:constraint) { double('constraint') }
97
- let(:dependency) { double('dependency', name: name, version_constraint: constraint) }
98
- subject { Class.new(Berkshelf::Location::Base).new(dependency) }
99
-
100
- describe "#download" do
101
- context "when #do_download is not defined" do
102
- it "raises a AbstractFunction" do
103
- expect { subject.download }.to raise_error(Berkshelf::AbstractFunction)
104
- end
105
- end
106
-
107
- context "when #do_download is defined" do
108
- let(:cached) { double('cached') }
109
- before { subject.stub(do_download: cached) }
110
-
111
- it "validates the returned cached cookbook" do
112
- subject.should_receive(:validate_cached).with(cached).and_return(true)
113
- subject.download
114
- end
115
-
116
- it "returns the cached cookbook if valid" do
117
- subject.stub(validate_cached: true)
118
-
119
- expect(subject.download).to eq(cached)
120
- end
121
- end
122
- end
123
-
124
- describe '#validate_cached' do
125
- let(:cached) { double('cached-cb', cookbook_name: name, version: '0.1.0') }
126
-
127
- it 'raises a CookbookValidationFailure error if the version constraint does not satisfy the cached version' do
128
- constraint.should_receive(:satisfies?).with(cached.version).and_return(false)
129
-
130
- expect {
131
- subject.validate_cached(cached)
132
- }.to raise_error(Berkshelf::CookbookValidationFailure)
133
- end
134
-
135
- it 'returns true if cached_cookbooks satisfies the version constraint' do
136
- constraint.should_receive(:satisfies?).with(cached.version).and_return(true)
137
- expect(subject.validate_cached(cached)).to be_true
138
- end
139
-
140
- context "when the cached_cookbooks satisfies the version constraint" do
141
- it "returns true if the name of the cached_cookbook matches the name of the location" do
142
- constraint.should_receive(:satisfies?).with(cached.version).and_return(true)
143
- cached.stub(:name) { name }
144
- expect(subject.validate_cached(cached)).to be_true
145
- end
146
-
147
- it "warns about the MismatchedCookbookName if the cached_cookbook's name does not match the location's" do
148
- constraint.should_receive(:satisfies?).with(cached.version).and_return(true)
149
- cached.stub(:cookbook_name) { "artifact" }
150
- msg = Berkshelf::MismatchedCookbookName.new(dependency, cached).to_s
151
22
 
152
- Berkshelf.ui.should_receive(:warn).with(msg)
153
- subject.validate_cached(cached)
23
+ it 'returns nil when a location cannot be found' do
24
+ instance = described_class.init(dependency, lamesauce: 'meats/bacon')
25
+ expect(instance).to be_nil
154
26
  end
155
27
  end
156
28
  end
@@ -0,0 +1,80 @@
1
+ require 'spec_helper'
2
+
3
+ module Berkshelf
4
+ describe BaseLocation do
5
+ let(:constraint) { double('constraint') }
6
+ let(:dependency) { double('dependency', name: 'cookbook', version_constraint: constraint) }
7
+
8
+ subject { described_class.new(dependency) }
9
+
10
+ describe '#installed?' do
11
+ it 'is an abstract function' do
12
+ expect { subject.installed? }.to raise_error(AbstractFunction)
13
+ end
14
+ end
15
+
16
+ describe '#install' do
17
+ it 'is an abstract function' do
18
+ expect { subject.install }.to raise_error(AbstractFunction)
19
+ end
20
+ end
21
+
22
+ describe '#cached_cookbook' do
23
+ it 'is an abstract function' do
24
+ expect { subject.cached_cookbook }.to raise_error(AbstractFunction)
25
+ end
26
+ end
27
+
28
+ describe '#to_lock' do
29
+ it 'is an abstract function' do
30
+ expect { subject.to_lock }.to raise_error(AbstractFunction)
31
+ end
32
+ end
33
+
34
+ describe '#validate_cached!' do
35
+ context 'when the path is not a cookbook' do
36
+ before { File.stub(:cookbook?).and_return(false) }
37
+
38
+ it 'raises an error' do
39
+ expect {
40
+ subject.validate_cached!('/foo/bar')
41
+ }.to raise_error(NotACookbook)
42
+ end
43
+ end
44
+
45
+ context 'when the path is a cookbook' do
46
+ let(:cookbook) do
47
+ double('cookbook',
48
+ cookbook_name: 'cookbook',
49
+ version: '0.1.0',
50
+ )
51
+ end
52
+
53
+ before do
54
+ File.stub(:cookbook?).and_return(true)
55
+ CachedCookbook.stub(:from_path).and_return(cookbook)
56
+ end
57
+
58
+ it 'raises an error if the constraint does not satisfy' do
59
+ constraint.stub(:satisfies?).with('0.1.0').and_return(false)
60
+ expect {
61
+ subject.validate_cached!(cookbook)
62
+ }.to raise_error(CookbookValidationFailure)
63
+ end
64
+
65
+ it 'raises an error if the names do not match' do
66
+ constraint.stub(:satisfies?).with('0.1.0').and_return(true)
67
+ cookbook.stub(:cookbook_name).and_return('different_name')
68
+ expect {
69
+ subject.validate_cached!(cookbook)
70
+ }.to raise_error(MismatchedCookbookName)
71
+ end
72
+
73
+ it 'returns true when the validation succeeds' do
74
+ constraint.stub(:satisfies?).with('0.1.0').and_return(true)
75
+ expect(subject.validate_cached!(cookbook)).to be_true
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,249 @@
1
+ require 'spec_helper'
2
+
3
+ module Berkshelf
4
+ describe GitLocation do
5
+ let(:dependency) { double(name: 'bacon') }
6
+
7
+ subject do
8
+ described_class.new(dependency, git: 'https://repo.com', branch: 'ham',
9
+ tag: 'v1.2.3', ref: 'abc123', revision: 'defjkl123456', rel: 'hi')
10
+ end
11
+
12
+ describe '.initialize' do
13
+ it 'sets the uri' do
14
+ instance = described_class.new(dependency, git: 'https://repo.com')
15
+ expect(instance.uri).to eq('https://repo.com')
16
+ end
17
+
18
+ it 'sets the branch' do
19
+ instance = described_class.new(dependency,
20
+ git: 'https://repo.com', branch: 'magic_new_feature')
21
+ expect(instance.branch).to eq('magic_new_feature')
22
+ end
23
+
24
+ it 'sets the tag' do
25
+ instance = described_class.new(dependency,
26
+ git: 'https://repo.com', tag: 'v1.2.3')
27
+ expect(instance.tag).to eq('v1.2.3')
28
+ end
29
+
30
+ it 'adds the ref' do
31
+ instance = described_class.new(dependency,
32
+ git: 'https://repo.com', ref: 'abc123')
33
+ expect(instance.ref).to eq('abc123')
34
+ end
35
+
36
+ it 'sets the revision' do
37
+ instance = described_class.new(dependency,
38
+ git: 'https://repo.com', revision: 'abcde12345')
39
+ expect(instance.revision).to eq('abcde12345')
40
+ end
41
+
42
+ it 'sets the rel' do
43
+ instance = described_class.new(dependency,
44
+ git: 'https://repo.com', rel: 'internal/path')
45
+ expect(instance.rel).to eq('internal/path')
46
+ end
47
+
48
+ context 'rev_parse' do
49
+ def rev_parse(instance)
50
+ instance.instance_variable_get(:@rev_parse)
51
+ end
52
+
53
+ it 'uses the :ref option with priority' do
54
+ instance = described_class.new(dependency,
55
+ git: 'https://repo.com', ref: 'abc123', branch: 'magic_new_feature')
56
+ expect(rev_parse(instance)).to eq('abc123')
57
+ end
58
+
59
+ it 'uses the :branch option with priority' do
60
+ instance = described_class.new(dependency,
61
+ git: 'https://repo.com', branch: 'magic_new_feature', tag: 'v1.2.3')
62
+ expect(rev_parse(instance)).to eq('magic_new_feature')
63
+ end
64
+
65
+ it 'uses the :tag option' do
66
+ instance = described_class.new(dependency,
67
+ git: 'https://repo.com', tag: 'v1.2.3')
68
+ expect(rev_parse(instance)).to eq('v1.2.3')
69
+ end
70
+
71
+ it 'uses "master" when none is given' do
72
+ instance = described_class.new(dependency, git: 'https://repo.com')
73
+ expect(rev_parse(instance)).to eq('master')
74
+ end
75
+ end
76
+ end
77
+
78
+ describe '#installed?' do
79
+ it 'returns false when there is no revision' do
80
+ subject.stub(:revision).and_return(nil)
81
+ expect(subject.installed?).to be_false
82
+ end
83
+
84
+ it 'returns false when the install_path does not exist' do
85
+ subject.stub(:revision).and_return('abcd1234')
86
+ subject.stub(:install_path).and_return(double(exist?: false))
87
+ expect(subject.installed?).to be_false
88
+ end
89
+
90
+ it 'returns true when the location is installed' do
91
+ subject.stub(:revision).and_return('abcd1234')
92
+ subject.stub(:install_path).and_return(double(exist?: true))
93
+ expect(subject.installed?).to be_true
94
+ end
95
+ end
96
+
97
+ describe '#install' do
98
+ before do
99
+ File.stub(:chmod)
100
+ FileUtils.stub(:cp_r)
101
+ subject.stub(:validate_cached!)
102
+ subject.stub(:git)
103
+ end
104
+
105
+ context 'when the repository is cached' do
106
+ it 'pulls a new version' do
107
+ Dir.stub(:chdir) { |args, &b| b.call } # Force eval the chdir block
108
+
109
+ subject.stub(:cached?).and_return(true)
110
+ expect(subject).to receive(:git).with(
111
+ 'fetch --force --tags https://repo.com "refs/heads/*:refs/heads/*"'
112
+ )
113
+ subject.install
114
+ end
115
+ end
116
+
117
+ context 'when the revision is not cached' do
118
+ it 'clones the repository' do
119
+ Dir.stub(:chdir) { |args, &b| b.call } # Force eval the chdir block
120
+
121
+ cache_path = subject.send(:cache_path)
122
+ subject.stub(:cached?).and_return(false)
123
+ expect(subject).to receive(:git).with(
124
+ %|clone https://repo.com "#{cache_path}" --bare --no-hardlinks|
125
+ )
126
+ subject.install
127
+ end
128
+ end
129
+ end
130
+
131
+ describe '#cached_cookbook' do
132
+ it 'returns nil if the cookbook is not installed' do
133
+ subject.stub(:installed?).and_return(false)
134
+ expect(subject.cached_cookbook).to be_nil
135
+ end
136
+
137
+ it 'returns the cookbook at the install_path' do
138
+ subject.stub(:installed?).and_return(true)
139
+ CachedCookbook.stub(:from_path)
140
+
141
+ expect(CachedCookbook).to receive(:from_path).once
142
+ subject.cached_cookbook
143
+ end
144
+ end
145
+
146
+ describe '#==' do
147
+ let(:other) { subject.dup }
148
+
149
+ it 'returns true when everything matches' do
150
+ expect(subject).to eq(other)
151
+ end
152
+
153
+ it 'returns false when the other location is not an GitLocation' do
154
+ other.stub(:is_a?).and_return(false)
155
+ expect(subject).to_not eq(other)
156
+ end
157
+
158
+ it 'returns false when the uri is different' do
159
+ other.stub(:uri).and_return('different')
160
+ expect(subject).to_not eq(other)
161
+ end
162
+
163
+ it 'returns false when the branch is different' do
164
+ other.stub(:branch).and_return('different')
165
+ expect(subject).to_not eq(other)
166
+ end
167
+
168
+ it 'returns false when the tag is different' do
169
+ other.stub(:tag).and_return('different')
170
+ expect(subject).to_not eq(other)
171
+ end
172
+
173
+ it 'returns false when the ref is different' do
174
+ other.stub(:ref).and_return('different')
175
+ expect(subject).to_not eq(other)
176
+ end
177
+
178
+ it 'returns false when the rel is different' do
179
+ other.stub(:rel).and_return('different')
180
+ expect(subject).to_not eq(other)
181
+ end
182
+ end
183
+
184
+ describe '#to_s' do
185
+ it 'prefers the tag' do
186
+ expect(subject.to_s).to eq('https://repo.com (at v1.2.3/hi)')
187
+ end
188
+
189
+ it 'prefers the branch' do
190
+ subject.stub(:tag).and_return(nil)
191
+ expect(subject.to_s).to eq('https://repo.com (at ham/hi)')
192
+ end
193
+
194
+ it 'falls back to the ref' do
195
+ subject.stub(:tag).and_return(nil)
196
+ subject.stub(:branch).and_return(nil)
197
+ expect(subject.to_s).to eq('https://repo.com (at abc123/hi)')
198
+ end
199
+
200
+ it 'does not use the rel if missing' do
201
+ subject.stub(:rel).and_return(nil)
202
+ expect(subject.to_s).to eq('https://repo.com (at v1.2.3)')
203
+ end
204
+ end
205
+
206
+ describe '#to_lock' do
207
+ it 'includes all the information' do
208
+ expect(subject.to_lock).to eq <<-EOH.gsub(/^ {8}/, '')
209
+ git: https://repo.com
210
+ revision: defjkl123456
211
+ ref: abc123
212
+ branch: ham
213
+ tag: v1.2.3
214
+ rel: hi
215
+ EOH
216
+ end
217
+
218
+ it 'does not include the branch if missing' do
219
+ subject.stub(:branch).and_return(nil)
220
+ expect(subject.to_lock).to_not include('branch')
221
+ end
222
+
223
+ it 'does not include the tag if missing' do
224
+ subject.stub(:tag).and_return(nil)
225
+ expect(subject.to_lock).to_not include('tag')
226
+ end
227
+
228
+ it 'does not include the rel if missing' do
229
+ subject.stub(:rel).and_return(nil)
230
+ expect(subject.to_lock).to_not include('rel')
231
+ end
232
+ end
233
+
234
+ describe '#git' do
235
+ before { described_class.send(:public, :git) }
236
+
237
+ it 'raises an error if Git is not installed' do
238
+ Berkshelf.stub(:which).and_return(false)
239
+ expect { subject.git('foo') }.to raise_error(GitLocation::GitNotInstalled)
240
+ end
241
+
242
+ it 'raises an error if the command fails' do
243
+ shell_out = double('shell_out', success?: false, stderr: nil)
244
+ Buff::ShellOut.stub(:shell_out).and_return(shell_out)
245
+ expect { subject.git('foo') }.to raise_error(GitLocation::GitCommandError)
246
+ end
247
+ end
248
+ end
249
+ end