Due to the Vagrantfile being loaded prior to plugin loading to determine project local plugin information the Vagrant.has_plugin? helper will always return false when the Vagrantfile is first loaded. To prevent this behavior we can check for plugins in the plugin data files prior to the plugins being loaded, and after they have been loaded we can fallback to the original specification based check.
441 lines
13 KiB
Ruby
441 lines
13 KiB
Ruby
require "json"
|
|
require "pathname"
|
|
|
|
require "vagrant/plugin"
|
|
require "vagrant/plugin/manager"
|
|
require "vagrant/plugin/state_file"
|
|
require "vagrant/util/deep_merge"
|
|
require File.expand_path("../../../base", __FILE__)
|
|
|
|
describe Vagrant::Plugin::Manager do
|
|
include_context "unit"
|
|
|
|
let(:path) do
|
|
Pathname.new(Dir::Tmpname.create("vagrant-test-plugin-manager") {})
|
|
end
|
|
|
|
let(:bundler) { double("bundler") }
|
|
|
|
after do
|
|
path.unlink if path.file?
|
|
end
|
|
|
|
before do
|
|
allow(Vagrant::Bundler).to receive(:instance).and_return(bundler)
|
|
end
|
|
|
|
subject { described_class.new(path) }
|
|
|
|
describe "#globalize!" do
|
|
let(:plugins) { double("plugins") }
|
|
|
|
before do
|
|
allow(subject).to receive(:bundler_init)
|
|
allow(subject).to receive(:installed_plugins).and_return(plugins)
|
|
end
|
|
|
|
it "should init bundler with installed plugins" do
|
|
expect(subject).to receive(:bundler_init).with(plugins)
|
|
subject.globalize!
|
|
end
|
|
|
|
it "should return installed plugins" do
|
|
expect(subject.globalize!).to eq(plugins)
|
|
end
|
|
end
|
|
|
|
describe "#localize!" do
|
|
let(:env) { double("env", local_data_path: local_data_path) }
|
|
let(:local_data_path) { double("local_data_path") }
|
|
let(:plugins) { double("plugins") }
|
|
let(:state_file) { double("state_file", installed_plugins: plugins) }
|
|
|
|
before do
|
|
allow(Vagrant::Plugin::StateFile).to receive(:new).and_return(state_file)
|
|
allow(bundler).to receive(:environment_path=)
|
|
allow(local_data_path).to receive(:join).and_return(local_data_path)
|
|
allow(subject).to receive(:bundler_init)
|
|
end
|
|
|
|
context "without local data path defined" do
|
|
let(:local_data_path) { nil }
|
|
|
|
it "should not do any initialization" do
|
|
expect(subject).not_to receive(:bundler_init)
|
|
subject.localize!(env)
|
|
end
|
|
|
|
it "should return nil" do
|
|
expect(subject.localize!(env)).to be_nil
|
|
end
|
|
end
|
|
|
|
it "should run bundler initialization" do
|
|
expect(subject).to receive(:bundler_init).with(plugins)
|
|
subject.localize!(env)
|
|
end
|
|
|
|
it "should return plugins" do
|
|
expect(subject.localize!(env)).to eq(plugins)
|
|
end
|
|
end
|
|
|
|
describe "#ready?" do
|
|
let(:plugins) { double("plugins") }
|
|
let(:env) { double("env", local_data_path: nil) }
|
|
|
|
before do
|
|
allow(subject).to receive(:bundler_init)
|
|
end
|
|
|
|
it "should be false by default" do
|
|
expect(subject.ready?).to be_falsey
|
|
end
|
|
|
|
it "should be false when only globalize! has been called" do
|
|
subject.globalize!
|
|
expect(subject.ready?).to be_falsey
|
|
end
|
|
|
|
it "should be false when only localize! has been called" do
|
|
subject.localize!(env)
|
|
expect(subject.ready?).to be_falsey
|
|
end
|
|
|
|
it "should be true when both localize! and globalize! have been called" do
|
|
subject.globalize!
|
|
subject.localize!(env)
|
|
expect(subject.ready?).to be_truthy
|
|
end
|
|
end
|
|
|
|
describe "#bundler_init" do
|
|
let(:plugins) { {"plugin_name" => {}} }
|
|
|
|
before do
|
|
allow(Vagrant).to receive(:plugins_init?).and_return(true)
|
|
allow(bundler).to receive(:init!)
|
|
end
|
|
|
|
it "should init the bundler instance with plugins" do
|
|
expect(bundler).to receive(:init!).with(plugins)
|
|
subject.bundler_init(plugins)
|
|
end
|
|
|
|
it "should return nil" do
|
|
expect(subject.bundler_init(plugins)).to be_nil
|
|
end
|
|
|
|
context "with plugin init disabled" do
|
|
before { expect(Vagrant).to receive(:plugins_init?).and_return(false) }
|
|
|
|
it "should return nil" do
|
|
expect(subject.bundler_init(plugins)).to be_nil
|
|
end
|
|
|
|
it "should not init the bundler instance" do
|
|
expect(bundler).not_to receive(:init!).with(plugins)
|
|
subject.bundler_init(plugins)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#plugin_installed?" do
|
|
let(:ready) { true }
|
|
let(:specs) { [] }
|
|
|
|
before do
|
|
allow(subject).to receive(:ready?).and_return(ready)
|
|
allow(subject).to receive(:installed_specs).and_return(specs)
|
|
end
|
|
|
|
context "when manager is ready" do
|
|
it "should return false when plugin is not found" do
|
|
expect(subject.plugin_installed?("vagrant-plugin")).to be_falsey
|
|
end
|
|
|
|
context "when plugin is installed" do
|
|
let(:specs) { [Gem::Specification.new("vagrant-plugin", "1.2.3")] }
|
|
|
|
it "should return true" do
|
|
expect(subject.plugin_installed?("vagrant-plugin")).to be_truthy
|
|
end
|
|
|
|
it "should return true when version matches installed version" do
|
|
expect(subject.plugin_installed?("vagrant-plugin", "1.2.3")).to be_truthy
|
|
end
|
|
|
|
it "should return true when version requirement is satisified by version" do
|
|
expect(subject.plugin_installed?("vagrant-plugin", "> 1.0")).to be_truthy
|
|
end
|
|
|
|
it "should return false when version requirement is not satisified by version" do
|
|
expect(subject.plugin_installed?("vagrant-plugin", "2.0")).to be_falsey
|
|
end
|
|
end
|
|
end
|
|
|
|
context "when manager is not ready" do
|
|
let(:ready) { false }
|
|
let(:plugins) { {} }
|
|
before { allow(subject).to receive(:installed_plugins).and_return(plugins) }
|
|
|
|
it "should check installed plugin data" do
|
|
expect(subject).to receive(:installed_plugins).and_return(plugins)
|
|
subject.plugin_installed?("vagrant-plugin")
|
|
end
|
|
|
|
it "should return false when plugin is not found" do
|
|
expect(subject.plugin_installed?("vagrant-plugin")).to be_falsey
|
|
end
|
|
|
|
context "when plugin is installed" do
|
|
let(:plugins) { {"vagrant-plugin" => {"installed_gem_version" => "1.2.3"}} }
|
|
|
|
it "should return true" do
|
|
expect(subject.plugin_installed?("vagrant-plugin")).to be_truthy
|
|
end
|
|
|
|
it "should return true when version matches installed version" do
|
|
expect(subject.plugin_installed?("vagrant-plugin", "1.2.3")).to be_truthy
|
|
end
|
|
|
|
it "should return true when version requirement is satisified by version" do
|
|
expect(subject.plugin_installed?("vagrant-plugin", "> 1.0")).to be_truthy
|
|
end
|
|
|
|
it "should return false when version requirement is not satisified by version" do
|
|
expect(subject.plugin_installed?("vagrant-plugin", "2.0")).to be_falsey
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#install_plugin" do
|
|
it "installs the plugin and adds it to the state file" do
|
|
specs = Array.new(5) { Gem::Specification.new }
|
|
specs[3].name = "foo"
|
|
expect(bundler).to receive(:install).once.with(any_args) { |plugins, local|
|
|
expect(plugins).to have_key("foo")
|
|
expect(local).to be_falsey
|
|
}.and_return(specs)
|
|
expect(bundler).to receive(:clean)
|
|
|
|
result = subject.install_plugin("foo")
|
|
|
|
# It should return the spec of the installed plugin
|
|
expect(result).to eql(specs[3])
|
|
|
|
# It should've added the plugin to the state
|
|
expect(subject.installed_plugins).to have_key("foo")
|
|
end
|
|
|
|
it "masks GemNotFound with our error" do
|
|
expect(bundler).to receive(:install).and_raise(Gem::GemNotFoundException)
|
|
|
|
expect { subject.install_plugin("foo") }.
|
|
to raise_error(Vagrant::Errors::PluginGemNotFound)
|
|
end
|
|
|
|
it "masks bundler errors with our own error" do
|
|
expect(bundler).to receive(:install).and_raise(Gem::InstallError)
|
|
|
|
expect { subject.install_plugin("foo") }.
|
|
to raise_error(Vagrant::Errors::BundlerError)
|
|
end
|
|
|
|
it "can install a local gem" do
|
|
name = "foo.gem"
|
|
version = "1.0"
|
|
|
|
local_spec = Gem::Specification.new
|
|
local_spec.name = "bar"
|
|
local_spec.version = version
|
|
|
|
expect(bundler).to receive(:install_local).with(name, {}).
|
|
ordered.and_return(local_spec)
|
|
|
|
expect(bundler).not_to receive(:install)
|
|
expect(bundler).to receive(:clean)
|
|
|
|
subject.install_plugin(name)
|
|
|
|
plugins = subject.installed_plugins
|
|
expect(plugins).to have_key("bar")
|
|
expect(plugins["bar"]["gem_version"]).to eql("1.0")
|
|
end
|
|
|
|
describe "installation options" do
|
|
let(:specs) do
|
|
specs = Array.new(5) { Gem::Specification.new }
|
|
specs[3].name = "foo"
|
|
specs
|
|
end
|
|
|
|
before do
|
|
allow(bundler).to receive(:install).and_return(specs)
|
|
end
|
|
|
|
it "installs a version with constraints" do
|
|
expect(bundler).to receive(:install).once.with(any_args) { |plugins, local|
|
|
expect(plugins).to have_key("foo")
|
|
expect(plugins["foo"]["gem_version"]).to eql(">= 0.1.0")
|
|
expect(local).to be_falsey
|
|
}.and_return(specs)
|
|
expect(bundler).to receive(:clean)
|
|
|
|
subject.install_plugin("foo", version: ">= 0.1.0")
|
|
|
|
plugins = subject.installed_plugins
|
|
expect(plugins).to have_key("foo")
|
|
expect(plugins["foo"]["gem_version"]).to eql(">= 0.1.0")
|
|
end
|
|
|
|
it "installs with an exact version but doesn't constrain" do
|
|
expect(bundler).to receive(:install).once.with(any_args) { |plugins, local|
|
|
expect(plugins).to have_key("foo")
|
|
expect(plugins["foo"]["gem_version"]).to eql("0.1.0")
|
|
expect(local).to be_falsey
|
|
}.and_return(specs)
|
|
expect(bundler).to receive(:clean)
|
|
|
|
subject.install_plugin("foo", version: "0.1.0")
|
|
|
|
plugins = subject.installed_plugins
|
|
expect(plugins).to have_key("foo")
|
|
expect(plugins["foo"]["gem_version"]).to eql("0.1.0")
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#uninstall_plugin" do
|
|
it "removes the plugin from the state" do
|
|
sf = Vagrant::Plugin::StateFile.new(path)
|
|
sf.add_plugin("foo")
|
|
|
|
# Sanity
|
|
expect(subject.installed_plugins).to have_key("foo")
|
|
|
|
# Test
|
|
expect(bundler).to receive(:clean).once.with({})
|
|
|
|
# Remove it
|
|
subject.uninstall_plugin("foo")
|
|
expect(subject.installed_plugins).to_not have_key("foo")
|
|
end
|
|
|
|
it "masks bundler errors with our own error" do
|
|
sf = Vagrant::Plugin::StateFile.new(path)
|
|
sf.add_plugin("foo")
|
|
expect(bundler).to receive(:clean).and_raise(Gem::InstallError)
|
|
|
|
expect { subject.uninstall_plugin("foo") }.
|
|
to raise_error(Vagrant::Errors::BundlerError)
|
|
end
|
|
|
|
context "with a system file" do
|
|
let(:systems_path) { temporary_file }
|
|
|
|
before do
|
|
systems_path.unlink
|
|
|
|
allow(described_class).to receive(:system_plugins_file).and_return(systems_path)
|
|
|
|
sf = Vagrant::Plugin::StateFile.new(systems_path)
|
|
sf.add_plugin("foo", version: "0.2.0")
|
|
sf.add_plugin("bar")
|
|
end
|
|
|
|
it "uninstalls the user plugin if it exists" do
|
|
sf = Vagrant::Plugin::StateFile.new(path)
|
|
sf.add_plugin("bar")
|
|
|
|
# Test
|
|
expect(bundler).to receive(:clean).once.with(anything)
|
|
|
|
# Remove it
|
|
subject.uninstall_plugin("bar")
|
|
|
|
plugins = subject.installed_plugins
|
|
expect(plugins["foo"]["system"]).to be(true)
|
|
end
|
|
|
|
it "raises an error if uninstalling a system gem" do
|
|
expect { subject.uninstall_plugin("bar") }.
|
|
to raise_error(Vagrant::Errors::PluginUninstallSystem)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#update_plugins" do
|
|
it "masks bundler errors with our own error" do
|
|
expect(bundler).to receive(:update).and_raise(Gem::InstallError)
|
|
|
|
expect { subject.update_plugins([]) }.
|
|
to raise_error(Vagrant::Errors::BundlerError)
|
|
end
|
|
end
|
|
|
|
context "without state" do
|
|
describe "#installed_plugins" do
|
|
it "is empty initially" do
|
|
expect(subject.installed_plugins).to be_empty
|
|
end
|
|
end
|
|
end
|
|
|
|
context "with state" do
|
|
before do
|
|
sf = Vagrant::Plugin::StateFile.new(path)
|
|
sf.add_plugin("foo", version: "0.1.0")
|
|
end
|
|
|
|
describe "#installed_plugins" do
|
|
it "has the plugins" do
|
|
plugins = subject.installed_plugins
|
|
expect(plugins.length).to eql(1)
|
|
expect(plugins).to have_key("foo")
|
|
end
|
|
end
|
|
|
|
describe "#installed_specs" do
|
|
it "has the plugins" do
|
|
# We just add "i18n" because it is a dependency of Vagrant and
|
|
# we know it will be there.
|
|
sf = Vagrant::Plugin::StateFile.new(path)
|
|
sf.add_plugin("i18n")
|
|
|
|
specs = subject.installed_specs
|
|
expect(specs.length).to eql(1)
|
|
expect(specs.first.name).to eql("i18n")
|
|
end
|
|
end
|
|
|
|
context "with system plugins" do
|
|
let(:systems_path) { temporary_file }
|
|
|
|
before do
|
|
systems_path.unlink
|
|
|
|
allow(described_class).to receive(:system_plugins_file).and_return(systems_path)
|
|
|
|
sf = Vagrant::Plugin::StateFile.new(systems_path)
|
|
sf.add_plugin("foo", version: "0.2.0")
|
|
sf.add_plugin("bar")
|
|
end
|
|
|
|
describe "#installed_plugins" do
|
|
it "has the plugins" do
|
|
plugins = subject.installed_plugins
|
|
expect(plugins.length).to eql(2)
|
|
expect(plugins).to have_key("foo")
|
|
expect(plugins["foo"]["gem_version"]).to eq("0.1.0")
|
|
expect(plugins["foo"]["system"]).to be_truthy
|
|
expect(plugins).to have_key("bar")
|
|
expect(plugins["bar"]["system"]).to be(true)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|