berkshelf 1.4.0.rc1 → 1.4.0

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