berkshelf 3.0.0.beta7 → 3.0.0.beta8

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 (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