require File.expand_path("../../../base", __FILE__) require "vagrant/util/downloader" describe Vagrant::Util::Downloader do let(:source) { "foo" } let(:destination) { "bar" } let(:exit_code) { 0 } let(:subprocess_result) do double("subprocess_result").tap do |result| result.stub(exit_code: exit_code) result.stub(stderr: "") end end subject { described_class.new(source, destination) } before :each do allow(Vagrant::Util::Subprocess).to receive(:execute).and_return(subprocess_result) end describe "#download!" do let(:curl_options) { ["-q", "--fail", "--location", "--max-redirs", "10", "--user-agent", described_class::USER_AGENT, "--output", destination, source, {}] } context "with a good exit status" do let(:exit_code) { 0 } it "downloads the file and returns true" do expect(Vagrant::Util::Subprocess).to receive(:execute). with("curl", *curl_options). and_return(subprocess_result) expect(subject.download!).to be end end context "with a bad exit status" do let(:exit_code) { 1 } it "raises an exception" do expect(Vagrant::Util::Subprocess).to receive(:execute). with("curl", *curl_options). and_return(subprocess_result) expect { subject.download! }. to raise_error(Vagrant::Errors::DownloaderError) end end context "with a username and password" do it "downloads the file with the proper flags" do original_source = source source = "http://foo:bar@baz.com/box.box" subject = described_class.new(source, destination) i = curl_options.index(original_source) curl_options[i] = "http://baz.com/box.box" i = curl_options.index("--output") curl_options.insert(i, "foo:bar") curl_options.insert(i, "-u") expect(Vagrant::Util::Subprocess).to receive(:execute). with("curl", *curl_options). and_return(subprocess_result) expect(subject.download!).to be_true end end context "with an urlescaped username and password" do it "downloads the file with unescaped credentials" do original_source = source source = "http://fo%5Eo:b%40r@baz.com/box.box" subject = described_class.new(source, destination) i = curl_options.index(original_source) curl_options[i] = "http://baz.com/box.box" i = curl_options.index("--output") curl_options.insert(i, "fo^o:b@r") curl_options.insert(i, "-u") expect(Vagrant::Util::Subprocess).to receive(:execute). with("curl", *curl_options). and_return(subprocess_result) expect(subject.download!).to be_true end end context "with checksum" do let(:checksum_expected_value){ 'MD5_CHECKSUM_VALUE' } let(:checksum_invalid_value){ 'INVALID_VALUE' } let(:digest){ double("digest") } before do allow(digest).to receive(:file).and_return(digest) end [Digest::MD5, Digest::SHA1].each do |klass| short_name = klass.to_s.split("::").last.downcase context "using #{short_name} digest" do subject { described_class.new(source, destination, short_name.to_sym => checksum_expected_value) } context "that matches expected value" do before do expect(klass).to receive(:new).and_return(digest) expect(digest).to receive(:hexdigest).and_return(checksum_expected_value) end it "should not raise an exception" do expect(subject.download!).to be_true end end context "that does not match expected value" do before do expect(klass).to receive(:new).and_return(digest) expect(digest).to receive(:hexdigest).and_return(checksum_invalid_value) end it "should raise an exception" do expect{ subject.download! }.to raise_error(Vagrant::Errors::DownloaderChecksumError) end end end end context "using both md5 and sha1 digests" do context "that both match expected values" do subject { described_class.new(source, destination, md5: checksum_expected_value, sha1: checksum_expected_value) } before do expect(Digest::MD5).to receive(:new).and_return(digest) expect(Digest::SHA1).to receive(:new).and_return(digest) expect(digest).to receive(:hexdigest).and_return(checksum_expected_value).exactly(2).times end it "should not raise an exception" do expect(subject.download!).to be_true end end context "that only sha1 matches expected value" do subject { described_class.new(source, destination, md5: checksum_invalid_value, sha1: checksum_expected_value) } before do allow(Digest::MD5).to receive(:new).and_return(digest) allow(Digest::SHA1).to receive(:new).and_return(digest) expect(digest).to receive(:hexdigest).and_return(checksum_expected_value).at_least(:once) end it "should raise an exception" do expect{ subject.download! }.to raise_error(Vagrant::Errors::DownloaderChecksumError) end end context "that only md5 matches expected value" do subject { described_class.new(source, destination, md5: checksum_expected_value, sha1: checksum_invalid_value) } before do allow(Digest::MD5).to receive(:new).and_return(digest) allow(Digest::SHA1).to receive(:new).and_return(digest) expect(digest).to receive(:hexdigest).and_return(checksum_expected_value).at_least(:once) end it "should raise an exception" do expect{ subject.download! }.to raise_error(Vagrant::Errors::DownloaderChecksumError) end end context "that none match expected value" do subject { described_class.new(source, destination, md5: checksum_invalid_value, sha1: checksum_invalid_value) } before do allow(Digest::MD5).to receive(:new).and_return(digest) allow(Digest::SHA1).to receive(:new).and_return(digest) expect(digest).to receive(:hexdigest).and_return(checksum_expected_value).at_least(:once) end it "should raise an exception" do expect{ subject.download! }.to raise_error(Vagrant::Errors::DownloaderChecksumError) end end end end end describe "#head" do let(:curl_options) { ["-q", "--fail", "--location", "--max-redirs", "10", "--user-agent", described_class::USER_AGENT, source, {}] } it "returns the output" do subprocess_result.stub(stdout: "foo") options = curl_options.dup options.unshift("-I") expect(Vagrant::Util::Subprocess).to receive(:execute). with("curl", *options).and_return(subprocess_result) expect(subject.head).to eq("foo") end end end