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 "#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 "#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