berkshelf 1.4.0.rc1 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. data/.gitignore +1 -0
  2. data/.ruby-version +1 -1
  3. data/berkshelf.gemspec +2 -1
  4. data/features/contingent_command.feature +18 -0
  5. data/features/cookbook_command.feature +13 -11
  6. data/features/info_command.feature +39 -0
  7. data/features/step_definitions/filesystem_steps.rb +32 -0
  8. data/features/step_definitions/gem_steps.rb +3 -2
  9. data/features/support/env.rb +2 -0
  10. data/generator_files/Berksfile.erb +5 -0
  11. data/generator_files/Vagrantfile.erb +10 -0
  12. data/generator_files/default_test.rb.erb +11 -0
  13. data/generator_files/helpers.rb.erb +7 -0
  14. data/lib/berkshelf.rb +4 -3
  15. data/lib/berkshelf/berksfile.rb +5 -15
  16. data/lib/berkshelf/cached_cookbook.rb +18 -0
  17. data/lib/berkshelf/chef/config.rb +21 -26
  18. data/lib/berkshelf/cli.rb +40 -5
  19. data/lib/berkshelf/community_rest.rb +17 -1
  20. data/lib/berkshelf/cookbook_generator.rb +4 -0
  21. data/lib/berkshelf/cookbook_source.rb +21 -18
  22. data/lib/berkshelf/init_generator.rb +13 -3
  23. data/lib/berkshelf/locations/github_location.rb +1 -1
  24. data/lib/berkshelf/locations/path_location.rb +1 -1
  25. data/lib/berkshelf/resolver.rb +10 -6
  26. data/lib/berkshelf/test.rb +37 -0
  27. data/lib/berkshelf/version.rb +1 -1
  28. data/spec/spec_helper.rb +5 -1
  29. data/spec/unit/berkshelf/community_rest_spec.rb +1 -0
  30. data/spec/unit/berkshelf/cookbook_source_spec.rb +201 -181
  31. data/spec/unit/berkshelf/init_generator_spec.rb +40 -1
  32. data/spec/unit/berkshelf/locations/path_location_spec.rb +10 -0
  33. data/spec/unit/berkshelf/resolver_spec.rb +6 -7
  34. data/spec/unit/berkshelf/ui_spec.rb +2 -1
  35. data/spec/unit/chef/config_spec.rb +79 -4
  36. metadata +34 -8
@@ -1,5 +1,6 @@
1
1
  require 'open-uri'
2
2
  require 'retryable'
3
+ require 'addressable/uri'
3
4
 
4
5
  module Berkshelf
5
6
  # @author Jamie Winsor <reset@riotgames.com>
@@ -12,7 +13,11 @@ module Berkshelf
12
13
  #
13
14
  # @return [String]
14
15
  def unpack(target, destination = Dir.mktmpdir)
15
- Archive::Tar::Minitar.unpack(Zlib::GzipReader.new(File.open(target, 'rb')), destination)
16
+ if is_gzip_file(target)
17
+ Archive::Tar::Minitar.unpack(Zlib::GzipReader.new(File.open(target, 'rb')), destination)
18
+ elsif is_tar_file(target)
19
+ Archive::Tar::Minitar.unpack(target, destination)
20
+ end
16
21
  destination
17
22
  end
18
23
 
@@ -29,6 +34,17 @@ module Berkshelf
29
34
  def version_from_uri(uri)
30
35
  File.basename(uri.to_s).gsub('_', '.')
31
36
  end
37
+
38
+ private
39
+ def is_gzip_file(path)
40
+ # You cannot write "\x1F\x8B" because the default encoding of
41
+ # ruby >= 1.9.3 is UTF-8 and 8B is an invalid in UTF-8.
42
+ IO.binread(path, 2) == [0x1F, 0x8B].pack("C*")
43
+ end
44
+
45
+ def is_tar_file(path)
46
+ IO.binread(path, 8, 257) == "ustar \0"
47
+ end
32
48
  end
33
49
 
34
50
  V1_API = 'http://cookbooks.opscode.com/api/v1/cookbooks'.freeze
@@ -17,6 +17,10 @@ module Berkshelf
17
17
  type: :boolean,
18
18
  default: false
19
19
 
20
+ class_option :chef_minitest,
21
+ type: :boolean,
22
+ default: false
23
+
20
24
  class_option :scmversion,
21
25
  type: :boolean,
22
26
  default: false
@@ -68,6 +68,7 @@ module Berkshelf
68
68
 
69
69
  extend Forwardable
70
70
 
71
+ attr_reader :berksfile
71
72
  attr_reader :name
72
73
  attr_reader :options
73
74
  attr_reader :version_constraint
@@ -93,11 +94,12 @@ module Berkshelf
93
94
  # @option options [String] :tag
94
95
  # same as tag
95
96
  # @option options [String] :locked_version
96
- def initialize(name, options = {})
97
+ def initialize(berksfile, name, options = {})
97
98
  self.class.validate_options(options)
98
99
 
99
- @name = name
100
- @locked_version = Solve::Version.new(options[:locked_version]) if options[:locked_version]
100
+ @berksfile = berksfile
101
+ @name = name
102
+ @locked_version = Solve::Version.new(options[:locked_version]) if options[:locked_version]
101
103
  @version_constraint = Solve::Constraint.new(options[:locked_version] || options[:constraint] || ">= 0.0.0")
102
104
 
103
105
  @cached_cookbook, @location = cached_and_location(options)
@@ -115,6 +117,13 @@ module Berkshelf
115
117
  end
116
118
  end
117
119
 
120
+ # Determine the CachedCookbook and Location information from the given options.
121
+ #
122
+ # @return [Array<CachedCookbook, Location>]
123
+ def cached_and_location(options = {})
124
+ from_path(options) || from_cache(options) || from_default(options)
125
+ end
126
+
118
127
  # Returns true if the cookbook source has already been downloaded. A cookbook
119
128
  # source is downloaded when a cached cookbook is present.
120
129
  #
@@ -178,13 +187,6 @@ module Berkshelf
178
187
 
179
188
  private
180
189
 
181
- # Determine the CachedCookbook and Location information from the given options.
182
- #
183
- # @return [Array<CachedCookbook, Location>]
184
- def cached_and_location(options = {})
185
- from_path(options) || from_cache(options) || from_default(options)
186
- end
187
-
188
190
  # Attempt to load a CachedCookbook from a local file system path (if the :path
189
191
  # option was given). If one is found, the location and cached_cookbook is
190
192
  # updated. Otherwise, this method will raise a CookbookNotFound exception.
@@ -196,12 +198,13 @@ module Berkshelf
196
198
  def from_path(options = {})
197
199
  return nil unless options[:path]
198
200
 
199
- location = PathLocation.new(name, version_constraint, path: options[:path])
200
- cached = CachedCookbook.from_path(location.path)
201
+ path = File.expand_path(PathLocation.normalize_path(options[:path]), File.dirname(berksfile.filepath))
202
+ location = PathLocation.new(name, version_constraint, path: path)
203
+ cached = CachedCookbook.from_path(location.path)
201
204
 
202
- [cached, location]
203
- rescue IOError
204
- raise Berkshelf::CookbookNotFound
205
+ [ cached, location ]
206
+ rescue IOError => ex
207
+ raise Berkshelf::CookbookNotFound, ex
205
208
  end
206
209
 
207
210
  # Attempt to load a CachedCookbook from the local CookbookStore. This will save
@@ -214,9 +217,9 @@ module Berkshelf
214
217
  return nil unless File.exists?(path)
215
218
 
216
219
  location = PathLocation.new(name, version_constraint, path: path)
217
- cached = CachedCookbook.from_path(path, name: name)
220
+ cached = CachedCookbook.from_path(path, name: name)
218
221
 
219
- [cached, location]
222
+ [ cached, location ]
220
223
  end
221
224
 
222
225
  # Use the default location, and a nil CachedCookbook. If there is no location
@@ -230,7 +233,7 @@ module Berkshelf
230
233
  location = Location.init(name, version_constraint, options)
231
234
  end
232
235
 
233
- [nil, location]
236
+ [ nil, location ]
234
237
  end
235
238
 
236
239
  # The hypothetical location of this CachedCookbook, if it were to exist.
@@ -29,6 +29,10 @@ module Berkshelf
29
29
  type: :boolean,
30
30
  default: false
31
31
 
32
+ class_option :chef_minitest,
33
+ type: :boolean,
34
+ default: false
35
+
32
36
  class_option :scmversion,
33
37
  type: :boolean,
34
38
  default: false
@@ -69,6 +73,12 @@ module Berkshelf
69
73
  template "Thorfile.erb", target.join("Thorfile")
70
74
  end
71
75
 
76
+ if options[:chef_minitest]
77
+ empty_directory target.join("files/default/tests/minitest/support")
78
+ template "default_test.rb.erb", target.join("files/default/tests/minitest/default_test.rb")
79
+ template "helpers.rb.erb", target.join("files/default/tests/minitest/support/helpers.rb")
80
+ end
81
+
72
82
  if options[:scmversion]
73
83
  create_file target.join("VERSION"), "0.1.0"
74
84
  end
@@ -108,7 +118,7 @@ module Berkshelf
108
118
  raise InvalidConfiguration.new Config.instance.errors
109
119
  end
110
120
  end
111
-
121
+
112
122
 
113
123
  # Check for supporting gems for provided options
114
124
  #
@@ -138,14 +148,14 @@ module Berkshelf
138
148
 
139
149
  # Warn if the supporting gem for a default is not installed
140
150
  #
141
- # @return [Boolean]
151
+ # @return [Boolean]
142
152
  def assert_default_supported(option, gem_name = option.to_s)
143
153
  unless options[option]
144
154
  begin
145
155
  Gem::Specification.find_by_name(gem_name)
146
156
  rescue Gem::LoadError
147
157
  Berkshelf.ui.warn "By default, this cookbook was generated to support #{gem_name}, however, #{gem_name} is not installed."
148
- Berkshelf.ui.warn "To skip support for #{gem_name}, use --#{option}"
158
+ Berkshelf.ui.warn "To skip support for #{gem_name}, use --#{option.to_s.gsub('_', '-')}"
149
159
  Berkshelf.ui.warn "To install #{gem_name}: gem install #{gem_name}"
150
160
  return false
151
161
  end
@@ -11,7 +11,7 @@ module Berkshelf
11
11
 
12
12
  # Wraps GitLocation allowing the short form GitHub repo identifier
13
13
  # to be used in place of the complete repo url.
14
- #
14
+ #
15
15
  # @see GitLocation#initialize for parameter documentation
16
16
  #
17
17
  # @option options [String] :github
@@ -36,7 +36,7 @@ module Berkshelf
36
36
  def initialize(name, version_constraint, options = {})
37
37
  @name = name
38
38
  @version_constraint = version_constraint
39
- @path = self.class.normalize_path(options[:path])
39
+ @path = options[:path]
40
40
  set_downloaded_status(true)
41
41
  end
42
42
 
@@ -3,16 +3,20 @@ module Berkshelf
3
3
  class Resolver
4
4
  extend Forwardable
5
5
 
6
+ # @return [Berkshelf::Berksfile]
7
+ attr_reader :berksfile
8
+ # @return [Solve::Graph]
6
9
  attr_reader :graph
7
10
 
8
- # @param [Downloader] downloader
11
+ # @param [Berkshelf::Berksfile] berksfile
9
12
  # @param [Hash] options
10
13
  #
11
14
  # @option options [Array<CookbookSource>, CookbookSource] sources
12
- def initialize(downloader, options = {})
13
- @downloader = downloader
14
- @graph = Solve::Graph.new
15
- @sources = Hash.new
15
+ def initialize(berksfile, options = {})
16
+ @berksfile = berksfile
17
+ @downloader = @berksfile.downloader
18
+ @graph = Solve::Graph.new
19
+ @sources = Hash.new
16
20
 
17
21
  # Dependencies need to be added AFTER the sources. If they are
18
22
  # not, then one of the dependencies of a source that is added
@@ -70,7 +74,7 @@ module Berkshelf
70
74
  source.cached_cookbook.dependencies.each do |name, constraint|
71
75
  next if has_source?(name)
72
76
 
73
- add_source(CookbookSource.new(name, constraint: constraint))
77
+ add_source(CookbookSource.new(berksfile, name, constraint: constraint))
74
78
  end
75
79
  end
76
80
 
@@ -0,0 +1,37 @@
1
+ module Berkshelf
2
+ # Because aruba runs in a sub-process, there's no easy way to share mocks and
3
+ # stubs across a run (See RiotGames/berkshelf#208). As a work-around, we pass
4
+ # "special" mocks and stubs into the TEST environment variable. This class
5
+ # parses and then requires the appropriate mocks during the run.
6
+ #
7
+ # @author Seth Vargo <sethvargo@gmail.com>
8
+ class Mocks
9
+ require 'rspec/mocks/standalone'
10
+
11
+ class << self
12
+ def env_keys
13
+ self.instance_methods(false).map { |key| key.to_s.upcase }
14
+ end
15
+ end
16
+
17
+ def initialize(keys)
18
+ keys.each do |key|
19
+ self.send(key.downcase.to_sym, ENV[key.to_s])
20
+ end
21
+ end
22
+
23
+ # Trick bundler into thinking gems are missing.
24
+ #
25
+ # @param [String] gems
26
+ # a CSV list of gems to be missing
27
+ def missing_gems(gems)
28
+ gems.split(',').each do |gem|
29
+ Gem::Specification.stub(:find_by_name).with(gem).and_raise(Gem::LoadError)
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ unless (keys = Berkshelf::Mocks.env_keys & ENV.keys).empty?
36
+ Berkshelf::Mocks.new(keys)
37
+ end
@@ -1,3 +1,3 @@
1
1
  module Berkshelf
2
- VERSION = "1.4.0.rc1"
2
+ VERSION = "1.4.0"
3
3
  end
@@ -1,3 +1,7 @@
1
+ # We set this variable to load additional test materials during cucumber
2
+ # runs, since aruba runs in a subprocess. See lib/berkshelf/test.rb
3
+ ENV['RUBY_ENV'] ||= 'test'
4
+
1
5
  require 'rubygems'
2
6
  require 'bundler'
3
7
  require 'spork'
@@ -11,7 +15,7 @@ Spork.prefork do
11
15
 
12
16
  APP_ROOT = File.expand_path('../../', __FILE__)
13
17
  ENV["BERKSHELF_PATH"] = File.join(APP_ROOT, "spec", "tmp", "berkshelf")
14
- ENV["BERKSHELF_CHEF_CONFIG"] = File.join(APP_ROOT, "spec", "tmp", "knife.rb")
18
+ ENV["BERKSHELF_CHEF_CONFIG"] = File.join(APP_ROOT, "spec", "knife.rb")
15
19
 
16
20
  Dir[File.join(APP_ROOT, "spec/support/**/*.rb")].each {|f| require f}
17
21
 
@@ -15,6 +15,7 @@ describe Berkshelf::CommunityREST, vcr: { record: :new_episodes, serialize_with:
15
15
 
16
16
  it 'unpacks the tar' do
17
17
  ::File.should_receive(:open).with(target, 'rb')
18
+ ::IO.should_receive(:binread).with(target, 2).and_return([0x1F, 0x8B].pack("C*"))
18
19
  Zlib::GzipReader.should_receive(:new).with(file)
19
20
  Archive::Tar::Minitar.should_receive(:unpack).with(gzip_reader, destination)
20
21
 
@@ -1,267 +1,287 @@
1
1
  require 'spec_helper'
2
2
 
3
- module Berkshelf
4
- describe CookbookSource do
5
- let(:cookbook_name) { "nginx" }
3
+ describe Berkshelf::CookbookSource do
4
+ let(:cookbook_name) { "nginx" }
5
+ let(:berksfile) { double('berksfile', filepath: fixtures_path.join("Berksfile").to_s) }
6
6
 
7
- describe "ClassMethods" do
8
- subject { CookbookSource }
7
+ describe "ClassMethods" do
8
+ subject { Berkshelf::CookbookSource }
9
9
 
10
- describe "::initialize" do
11
- context "given no location key (i.e. :git, :path, :site)" do
12
- let(:source) { subject.new(cookbook_name) }
10
+ describe "::initialize" do
11
+ context "given no location key (i.e. :git, :path, :site)" do
12
+ let(:source) { subject.new(berksfile, cookbook_name) }
13
13
 
14
- it "sets a nil valie for location" do
15
- source.location.should be_nil
16
- end
14
+ it "sets a nil valie for location" do
15
+ source.location.should be_nil
17
16
  end
17
+ end
18
18
 
19
- context 'given no value for :locked_version' do
20
- let(:source) { subject.new(cookbook_name) }
19
+ context 'given no value for :locked_version' do
20
+ let(:source) { subject.new(berksfile, cookbook_name) }
21
21
 
22
- it 'returns a wildcard match for any version' do
23
- expect(source.version_constraint.to_s).to eq('>= 0.0.0')
24
- end
22
+ it 'returns a wildcard match for any version' do
23
+ expect(source.version_constraint.to_s).to eq('>= 0.0.0')
25
24
  end
25
+ end
26
26
 
27
- context 'given a value for :locked_version' do
28
- let(:source) { subject.new(cookbook_name, locked_version: '1.2.3') }
27
+ context 'given a value for :locked_version' do
28
+ let(:source) { subject.new(berksfile, cookbook_name, locked_version: '1.2.3') }
29
29
 
30
- it 'returns the locked_version as the constraint' do
31
- expect(source.version_constraint.to_s).to eq('= 1.2.3')
32
- end
30
+ it 'returns the locked_version as the constraint' do
31
+ expect(source.version_constraint.to_s).to eq('= 1.2.3')
33
32
  end
33
+ end
34
34
 
35
- context 'given no value for :constraint' do
36
- let(:source) { subject.new(cookbook_name) }
35
+ context 'given no value for :constraint' do
36
+ let(:source) { subject.new(berksfile, cookbook_name) }
37
37
 
38
- it 'returns a wildcard match for any version' do
39
- expect(source.version_constraint.to_s).to eq('>= 0.0.0')
40
- end
38
+ it 'returns a wildcard match for any version' do
39
+ expect(source.version_constraint.to_s).to eq('>= 0.0.0')
41
40
  end
41
+ end
42
42
 
43
- context 'given a value for :constraint' do
44
- let(:source) { subject.new(cookbook_name, constraint: '~> 1.0.84') }
43
+ context 'given a value for :constraint' do
44
+ let(:source) { subject.new(berksfile, cookbook_name, constraint: '~> 1.0.84') }
45
45
 
46
- it 'returns a Solve::Constraint for the given version for version_constraint' do
47
- expect(source.version_constraint.to_s).to eq('~> 1.0.84')
48
- end
46
+ it 'returns a Solve::Constraint for the given version for version_constraint' do
47
+ expect(source.version_constraint.to_s).to eq('~> 1.0.84')
49
48
  end
49
+ end
50
50
 
51
- context 'given a value for :locked_version and :constraint' do
52
- let(:source) { subject.new(cookbook_name, constraint: '~> 1.0.84', locked_version: '1.2.3') }
51
+ context 'given a value for :locked_version and :constraint' do
52
+ let(:source) { subject.new(berksfile, cookbook_name, constraint: '~> 1.0.84', locked_version: '1.2.3') }
53
53
 
54
- it 'uses the :locked_version' do
55
- expect(source.version_constraint.to_s).to eq('= 1.2.3')
56
- end
54
+ it 'uses the :locked_version' do
55
+ expect(source.version_constraint.to_s).to eq('= 1.2.3')
57
56
  end
57
+ end
58
58
 
59
- context "given a location key :git" do
60
- let(:url) { "git://url_to_git" }
61
- let(:source) { subject.new(cookbook_name, git: url) }
59
+ context "given a location key :git" do
60
+ let(:url) { "git://url_to_git" }
61
+ let(:source) { subject.new(berksfile, cookbook_name, git: url) }
62
62
 
63
- it "initializes a GitLocation for location" do
64
- source.location.should be_a(GitLocation)
65
- end
63
+ it "initializes a GitLocation for location" do
64
+ source.location.should be_a(Berkshelf::GitLocation)
65
+ end
66
66
 
67
- it "points to the given Git URL" do
68
- source.location.uri.should eql(url)
69
- end
67
+ it "points to the given Git URL" do
68
+ source.location.uri.should eql(url)
70
69
  end
70
+ end
71
71
 
72
- context "given a location key :path" do
73
- context "given a value for path that contains a cookbook" do
74
- let(:path) { fixtures_path.join("cookbooks", "example_cookbook").to_s }
72
+ context "given a location key :path" do
73
+ context "given a value for path that contains a cookbook" do
74
+ let(:path) { fixtures_path.join("cookbooks", "example_cookbook").to_s }
75
75
 
76
- it "initializes a PathLocation for location" do
77
- subject.new(cookbook_name, path: path).location.should be_a(PathLocation)
78
- end
76
+ it "initializes a PathLocation for location" do
77
+ subject.new(berksfile, cookbook_name, path: path).location.should be_a(Berkshelf::PathLocation)
78
+ end
79
79
 
80
- it "points to the specified path" do
81
- subject.new(cookbook_name, path: path).location.path.should eql(path)
82
- end
80
+ it "points to the specified path" do
81
+ subject.new(berksfile, cookbook_name, path: path).location.path.should eql(path)
83
82
  end
83
+ end
84
84
 
85
- context "given a value for path that does not contain a cookbook" do
86
- let(:path) { "/does/not/exist" }
85
+ context "given a value for path that does not contain a cookbook" do
86
+ let(:path) { "/does/not/exist" }
87
87
 
88
- it "raises Berkshelf::CookbookNotFound" do
89
- lambda {
90
- subject.new(cookbook_name, path: path)
91
- }.should raise_error(Berkshelf::CookbookNotFound)
92
- end
88
+ it "raises Berkshelf::CookbookNotFound" do
89
+ lambda {
90
+ subject.new(berksfile, cookbook_name, path: path)
91
+ }.should raise_error(Berkshelf::CookbookNotFound)
93
92
  end
93
+ end
94
94
 
95
- context "given an invalid option" do
96
- it "raises BerkshelfError with a friendly message" do
97
- lambda {
98
- subject.new(cookbook_name, invalid_opt: "thisisnotvalid")
99
- }.should raise_error(Berkshelf::BerkshelfError, "Invalid options for Cookbook Source: 'invalid_opt'.")
100
- end
101
-
102
- it "raises BerkshelfError with a messaging containing all of the invalid options" do
103
- lambda {
104
- subject.new(cookbook_name, invalid_one: "one", invalid_two: "two")
105
- }.should raise_error(Berkshelf::BerkshelfError, "Invalid options for Cookbook Source: 'invalid_one', 'invalid_two'.")
106
- end
95
+ context "given an invalid option" do
96
+ it "raises BerkshelfError with a friendly message" do
97
+ lambda {
98
+ subject.new(berksfile, cookbook_name, invalid_opt: "thisisnotvalid")
99
+ }.should raise_error(Berkshelf::BerkshelfError, "Invalid options for Cookbook Source: 'invalid_opt'.")
107
100
  end
108
101
 
109
- describe "::add_valid_option" do
110
- before(:each) do
111
- @original = subject.class_variable_get :@@valid_options
112
- subject.class_variable_set :@@valid_options, []
113
- end
102
+ it "raises BerkshelfError with a messaging containing all of the invalid options" do
103
+ lambda {
104
+ subject.new(berksfile, cookbook_name, invalid_one: "one", invalid_two: "two")
105
+ }.should raise_error(Berkshelf::BerkshelfError, "Invalid options for Cookbook Source: 'invalid_one', 'invalid_two'.")
106
+ end
107
+ end
114
108
 
115
- after(:each) do
116
- subject.class_variable_set :@@valid_options, @original
117
- end
109
+ describe "::add_valid_option" do
110
+ before(:each) do
111
+ @original = subject.class_variable_get :@@valid_options
112
+ subject.class_variable_set :@@valid_options, []
113
+ end
118
114
 
119
- it "adds an option to the list of valid options" do
120
- subject.add_valid_option(:one)
115
+ after(:each) do
116
+ subject.class_variable_set :@@valid_options, @original
117
+ end
121
118
 
122
- subject.valid_options.should have(1).item
123
- subject.valid_options.should include(:one)
124
- end
119
+ it "adds an option to the list of valid options" do
120
+ subject.add_valid_option(:one)
125
121
 
126
- it "does not add duplicate options to the list of valid options" do
127
- subject.add_valid_option(:one)
128
- subject.add_valid_option(:one)
122
+ subject.valid_options.should have(1).item
123
+ subject.valid_options.should include(:one)
124
+ end
125
+
126
+ it "does not add duplicate options to the list of valid options" do
127
+ subject.add_valid_option(:one)
128
+ subject.add_valid_option(:one)
129
129
 
130
- subject.valid_options.should have(1).item
131
- subject.valid_options.should include(:one)
132
- end
130
+ subject.valid_options.should have(1).item
131
+ subject.valid_options.should include(:one)
133
132
  end
133
+ end
134
134
 
135
- describe "::add_location_key" do
136
- before(:each) do
137
- @original = subject.class_variable_get :@@location_keys
138
- subject.class_variable_set :@@location_keys, {}
139
- end
135
+ describe "::add_location_key" do
136
+ before(:each) do
137
+ @original = subject.class_variable_get :@@location_keys
138
+ subject.class_variable_set :@@location_keys, {}
139
+ end
140
140
 
141
- after(:each) do
142
- subject.class_variable_set :@@location_keys, @original
143
- end
141
+ after(:each) do
142
+ subject.class_variable_set :@@location_keys, @original
143
+ end
144
144
 
145
- it "adds a location key and the associated class to the list of valid locations" do
146
- subject.add_location_key(:git, subject.class)
145
+ it "adds a location key and the associated class to the list of valid locations" do
146
+ subject.add_location_key(:git, subject.class)
147
147
 
148
- subject.location_keys.should have(1).item
149
- subject.location_keys.should include(:git)
150
- subject.location_keys[:git].should eql(subject.class)
151
- end
148
+ subject.location_keys.should have(1).item
149
+ subject.location_keys.should include(:git)
150
+ subject.location_keys[:git].should eql(subject.class)
151
+ end
152
152
 
153
- it "does not add duplicate location keys to the list of location keys" do
154
- subject.add_location_key(:git, subject.class)
155
- subject.add_location_key(:git, subject.class)
153
+ it "does not add duplicate location keys to the list of location keys" do
154
+ subject.add_location_key(:git, subject.class)
155
+ subject.add_location_key(:git, subject.class)
156
156
 
157
- subject.location_keys.should have(1).item
158
- subject.location_keys.should include(:git)
159
- end
157
+ subject.location_keys.should have(1).item
158
+ subject.location_keys.should include(:git)
160
159
  end
161
160
  end
161
+ end
162
162
 
163
- context "given a location key :site" do
164
- let(:url) { "http://path_to_api/v1" }
165
- let(:source) { subject.new(cookbook_name, site: url) }
163
+ context "given a location key :site" do
164
+ let(:url) { "http://path_to_api/v1" }
165
+ let(:source) { subject.new(berksfile, cookbook_name, site: url) }
166
166
 
167
- it "initializes a SiteLocation for location" do
168
- source.location.should be_a(SiteLocation)
169
- end
167
+ it "initializes a SiteLocation for location" do
168
+ source.location.should be_a(Berkshelf::SiteLocation)
169
+ end
170
170
 
171
- it "points to the specified URI" do
172
- source.location.api_uri.to_s.should eql(url)
173
- end
171
+ it "points to the specified URI" do
172
+ source.location.api_uri.to_s.should eql(url)
174
173
  end
174
+ end
175
175
 
176
- context "given multiple location options" do
177
- it "raises with an Berkshelf::BerkshelfError" do
178
- lambda {
179
- subject.new(cookbook_name, site: "something", git: "something")
180
- }.should raise_error(Berkshelf::BerkshelfError)
181
- end
176
+ context "given multiple location options" do
177
+ it "raises with an Berkshelf::BerkshelfError" do
178
+ lambda {
179
+ subject.new(berksfile, cookbook_name, site: "something", git: "something")
180
+ }.should raise_error(Berkshelf::BerkshelfError)
182
181
  end
182
+ end
183
183
 
184
- context "given a group option containing a single group" do
185
- let(:group) { :production }
186
- let(:source) { subject.new(cookbook_name, group: group) }
184
+ context "given a group option containing a single group" do
185
+ let(:group) { :production }
186
+ let(:source) { subject.new(berksfile, cookbook_name, group: group) }
187
187
 
188
- it "assigns the single group to the groups attribute" do
189
- source.groups.should include(group)
190
- end
188
+ it "assigns the single group to the groups attribute" do
189
+ source.groups.should include(group)
191
190
  end
191
+ end
192
192
 
193
- context "given a group option containing an array of groups" do
194
- let(:groups) { [ :development, :test ] }
195
- let(:source) { subject.new(cookbook_name, group: groups) }
193
+ context "given a group option containing an array of groups" do
194
+ let(:groups) { [ :development, :test ] }
195
+ let(:source) { subject.new(berksfile, cookbook_name, group: groups) }
196
196
 
197
- it "assigns all the groups to the group attribute" do
198
- source.groups.should eql(groups)
199
- end
197
+ it "assigns all the groups to the group attribute" do
198
+ source.groups.should eql(groups)
200
199
  end
200
+ end
201
201
 
202
- context "given no group option" do
203
- let(:source) { subject.new(cookbook_name) }
202
+ context "given no group option" do
203
+ let(:source) { subject.new(berksfile, cookbook_name) }
204
204
 
205
- it "should have the default group assigned" do
206
- source.groups.should include(:default)
207
- end
205
+ it "should have the default group assigned" do
206
+ source.groups.should include(:default)
208
207
  end
209
208
  end
210
209
  end
210
+ end
211
211
 
212
- subject { CookbookSource.new(cookbook_name) }
212
+ subject { Berkshelf::CookbookSource.new(berksfile, cookbook_name) }
213
213
 
214
- describe '#add_group' do
215
- it "should store strings as symbols" do
216
- subject.add_group "foo"
217
- subject.groups.should =~ [:default, :foo]
218
- end
214
+ describe '#add_group' do
215
+ it "should store strings as symbols" do
216
+ subject.add_group "foo"
217
+ subject.groups.should =~ [:default, :foo]
218
+ end
219
219
 
220
- it "should not store duplicate groups" do
221
- subject.add_group "bar"
222
- subject.add_group "bar"
223
- subject.add_group :bar
224
- subject.groups.should =~ [:default, :bar]
225
- end
220
+ it "should not store duplicate groups" do
221
+ subject.add_group "bar"
222
+ subject.add_group "bar"
223
+ subject.add_group :bar
224
+ subject.groups.should =~ [:default, :bar]
225
+ end
226
226
 
227
- it "should add multiple groups" do
228
- subject.add_group "baz", "quux"
229
- subject.groups.should =~ [:default, :baz, :quux]
230
- end
227
+ it "should add multiple groups" do
228
+ subject.add_group "baz", "quux"
229
+ subject.groups.should =~ [:default, :baz, :quux]
230
+ end
231
231
 
232
- it "should handle multiple groups as an array" do
233
- subject.add_group ["baz", "quux"]
234
- subject.groups.should =~ [:default, :baz, :quux]
235
- end
232
+ it "should handle multiple groups as an array" do
233
+ subject.add_group ["baz", "quux"]
234
+ subject.groups.should =~ [:default, :baz, :quux]
236
235
  end
236
+ end
237
+
238
+ describe "#cached_and_location" do
239
+ let(:options) { Hash.new }
237
240
 
238
- describe "#downloaded?" do
239
- it "returns true if self.cached_cookbook is not nil" do
240
- subject.stub(:cached_cookbook) { double('cb') }
241
+ before do
242
+ Berkshelf::CachedCookbook.stub(:from_path).and_return(double('cached_cookbook'))
243
+ end
241
244
 
242
- subject.downloaded?.should be_true
245
+ context "when given a value for :path" do
246
+ before do
247
+ berksfile.stub(filepath: "/rspec/Berksfile")
248
+ options[:path] = "cookbooks/whatever"
243
249
  end
244
250
 
245
- it "returns false if self.cached_cookbook is nil" do
246
- subject.stub(:cached_cookbook) { nil }
251
+ it "returns a PathLocation with a path relative to the Berksfile's filepath" do
252
+ _, location = subject.cached_and_location(options)
247
253
 
248
- subject.downloaded?.should be_false
254
+ location.path.should eql("/rspec/cookbooks/whatever")
249
255
  end
250
256
  end
257
+ end
251
258
 
252
- describe "#to_s" do
253
- it "contains the name, constraint, and groups" do
254
- source = CookbookSource.new("artifact", constraint: "= 0.10.0")
259
+ describe "#downloaded?" do
260
+ it "returns true if self.cached_cookbook is not nil" do
261
+ subject.stub(:cached_cookbook) { double('cb') }
255
262
 
256
- source.to_s.should eql("artifact (= 0.10.0) groups: [:default]")
257
- end
263
+ subject.downloaded?.should be_true
264
+ end
258
265
 
259
- context "given a CookbookSource with an explicit location" do
260
- it "contains the name, constraint, groups, and location" do
261
- source = CookbookSource.new("artifact", constraint: "= 0.10.0", site: "http://cookbooks.opscode.com/api/v1/cookbooks")
266
+ it "returns false if self.cached_cookbook is nil" do
267
+ subject.stub(:cached_cookbook) { nil }
262
268
 
263
- source.to_s.should eql("artifact (= 0.10.0) groups: [:default] location: site: 'http://cookbooks.opscode.com/api/v1/cookbooks'")
264
- end
269
+ subject.downloaded?.should be_false
270
+ end
271
+ end
272
+
273
+ describe "#to_s" do
274
+ it "contains the name, constraint, and groups" do
275
+ source = Berkshelf::CookbookSource.new(berksfile, "artifact", constraint: "= 0.10.0")
276
+
277
+ source.to_s.should eql("artifact (= 0.10.0) groups: [:default]")
278
+ end
279
+
280
+ context "given a CookbookSource with an explicit location" do
281
+ it "contains the name, constraint, groups, and location" do
282
+ source = Berkshelf::CookbookSource.new(berksfile, "artifact", constraint: "= 0.10.0", site: "http://cookbooks.opscode.com/api/v1/cookbooks")
283
+
284
+ source.to_s.should eql("artifact (= 0.10.0) groups: [:default] location: site: 'http://cookbooks.opscode.com/api/v1/cookbooks'")
265
285
  end
266
286
  end
267
287
  end