spec/unit/berkshelf/downloader_spec.rb in berkshelf-0.4.0.rc4 vs spec/unit/berkshelf/downloader_spec.rb in berkshelf-0.4.0

- old
+ new

@@ -1,82 +1,210 @@ require 'spec_helper' module Berkshelf describe Downloader do + describe "ClassMethods" do + subject { Downloader } + + describe "::initialize" do + context "when no value for locations is given" do + it "sets the @locations instance variable to a blank array" do + downloader = subject.new(double('store')) + + downloader.instance_variable_get(:@locations).should be_a(Array) + downloader.instance_variable_get(:@locations).should have(0).items + end + end + + context "when an explicit value of locations is given" do + let(:locations) do + [ + { + type: :chef_api, + value: double('capi'), + options: double('capi_opts') + }, + { + type: :chef_api, + value: double('capi2'), + options: double('capi_opts2') + } + ] + end + + it "sets the @locations instance variable to the given locations" do + downloader = subject.new(double('store'), locations: locations) + + downloader.instance_variable_get(:@locations).should eql(locations) + end + end + end + end + subject { Downloader.new(CookbookStore.new(tmp_path)) } - let(:source) { CookbookSource.new("sparkle_motion") } - describe "#enqueue" do - it "should add a source to the queue" do - subject.enqueue(source) + describe "#download" do + let(:source) { double('source', name: "artifact", version_constraint: "= 0.10.0") } + let(:location) { double('location') } + let(:cached_cookbook) { double('cached') } - subject.queue.should have(1).source + context "when the source has a location" do + before(:each) do + source.stub(:location).and_return(location) + location.should_receive(:download).with(subject.storage_path).and_return(cached_cookbook) + source.should_receive(:cached_cookbook=).with(cached_cookbook) + end + + it "sends 'download' to the source's location and sets the source's cached_cookbook to the result" do + subject.download(source).should be_true + end + + it "returns an Array containing the cached_cookbook and location used to download" do + result = subject.download(source) + + result.should be_a(Array) + result[0].should eql(cached_cookbook) + result[1].should eql(location) + end end - it "should not allow you to add an invalid source" do - lambda { - subject.enqueue("a string, not a source") - }.should raise_error(ArgumentError) + context "when the source does not have a location" do + before(:each) do + source.stub(:location).and_return(nil) + subject.stub(:locations).and_return([{type: :chef_api, value: :knife, options: Hash.new}]) + end + + it "sends the 'download' message to the default location" do + Location.should_receive(:init).with(source.name, source.version_constraint, chef_api: :knife).and_return(location) + location.should_receive(:download).with(subject.storage_path).and_return(cached_cookbook) + source.should_receive(:cached_cookbook=).with(cached_cookbook) + + subject.download(source) + end end end - describe "#dequeue" do - before(:each) { subject.enqueue(source) } + describe "#locations" do + let(:type) { :site } + let(:value) { double('value') } + let(:options) { double('options') } - it "should remove a source from the queue" do - subject.dequeue(source) + it "returns an array of Hashes representing locations" do + subject.add_location(type, value, options) - subject.queue.should be_empty + subject.locations.each { |l| l.should be_a(Hash) } end + + context "when no locations are explicitly added" do + subject { Downloader.new(double('store')) } + + it "returns an array of default locations" do + subject.locations.should eql(Downloader::DEFAULT_LOCATIONS) + end + end + + context "when locations are explicitly added" do + let(:locations) do + [ + { + type: :chef_api, + value: double('capi'), + options: double('capi_opts') + }, + { + type: :chef_api, + value: double('capi2'), + options: double('capi_opts2') + } + ] + end + + subject { Downloader.new(double('store'), locations: locations) } + + it "contains only the locations passed to the initializer" do + subject.locations.should eql(locations) + end + + it "does not include the array of default locations" do + subject.locations.should_not include(Downloader::DEFAULT_LOCATIONS) + end + end end - describe "#download_all" do - let(:source_one) { CookbookSource.new("nginx") } - let(:source_two) { CookbookSource.new("mysql") } + describe "#add_location" do + let(:type) { :site } + let(:value) { double('value') } + let(:options) { double('options') } - before(:each) do - subject.enqueue source_one - subject.enqueue source_two + it "adds a hash to the end of the array of locations" do + subject.add_location(type, value, options) + + subject.locations.should have(1).item end - it "should remove each item from the queue after a successful download" do - subject.download_all + it "adds a hash with a type, value, and options key" do + subject.add_location(type, value, options) - subject.queue.should be_empty + subject.locations.last.should have_key(:type) + subject.locations.last.should have_key(:value) + subject.locations.last.should have_key(:options) end - it "should not remove the item from the queue if the download failed" do - subject.enqueue CookbookSource.new("does_not_exist_no_way") - subject.download_all + it "sets the value of the given 'value' to the value of the key 'value'" do + subject.add_location(type, value, options) - subject.queue.should have(1).sources + subject.locations.last[:value].should eql(value) end - it "should return a TXResultSet" do - results = subject.download_all + it "sets the value of the given 'type' to the value of the key 'type'" do + subject.add_location(type, value, options) - results.should be_a(TXResultSet) + subject.locations.last[:type].should eql(type) end - end - describe "#download" do - let(:source) { CookbookSource.new("nginx") } - let(:bad_source) { CookbookSource.new("donowaytexists") } + it "sets the value of the given 'options' to the value of the key 'options'" do + subject.add_location(type, value, options) - it "returns a TXResult" do - subject.download(source).should be_a(TXResult) + subject.locations.last[:options].should eql(options) end - context "when successful" do - it "returns a successesful TXResult" do - subject.download(source).should be_success - end + it "raises a DuplicateLocationDefined error if a location of the given type and value was already added" do + subject.add_location(type, value, options) + + lambda { + subject.add_location(type, value, options) + }.should raise_error(DuplicateLocationDefined) end - context "when failure" do - it "returns a failed TXResult" do - subject.download(bad_source).should be_failed + context "adding multiple locations" do + let(:type_2) { :site } + let(:value_2) { double('value_2') } + let(:options_2) { double('options_2') } + + it "adds locations in the order they are added" do + subject.add_location(type, value, options) + subject.add_location(type_2, value_2, options_2) + + subject.locations.should have(2).items + + subject.locations[0][:value].should eql(value) + subject.locations[1][:value].should eql(value_2) end + end + end + + describe "#has_location?" do + let(:type) { :site } + let(:value) { double('value') } + + it "returns true if a source of the given type and value was already added" do + subject.add_location(type, value) + + subject.has_location?(type, value).should be_true + end + + it "returns false if a source of the given type and value was not added" do + subject.has_location?(type, value).should be_false end end end end