# Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: BUSL-1.1 require File.expand_path("../../../../base", __FILE__) describe Vagrant::Action::Builtin::BoxRemove do include_context "unit" let(:app) { lambda { |env| } } let(:env) { { box_collection: box_collection, home_path: home_path, machine_index: machine_index, ui: Vagrant::UI::Silent.new, } } subject { described_class.new(app, env) } let(:box_collection) { double("box_collection") } let(:home_path) { double("home-path") } let(:machine_index) { [] } let(:iso_env) { isolated_environment } let(:box) do box_dir = iso_env.box3("foo", "1.0", :virtualbox) Vagrant::Box.new("foo", :virtualbox, "1.0", box_dir) end it "deletes the box if it is the only option" do allow(box_collection).to receive(:all).and_return([["foo", "1.0", :virtualbox, nil]]) env[:box_name] = "foo" expect(box_collection).to receive(:find).with( "foo", :virtualbox, "1.0", nil).and_return(box) expect(box_collection).to receive(:clean).with(box.name) .and_return(true) expect(box).to receive(:destroy!).once expect(app).to receive(:call).with(env).once subject.call(env) expect(env[:box_removed]).to equal(box) end it "deletes the box with the specified provider if given" do allow(box_collection).to receive(:all) .and_return([ ["foo", "1.0", :virtualbox], ["foo", "1.0", :vmware], ]) env[:box_name] = "foo" env[:box_provider] = "virtualbox" expect(box_collection).to receive(:find).with( "foo", :virtualbox, "1.0", nil).and_return(box) expect(box_collection).to receive(:clean).with(box.name) .and_return(false) expect(box).to receive(:destroy!).once expect(app).to receive(:call).with(env).once subject.call(env) expect(env[:box_removed]).to equal(box) end it "deletes the box with the specified version if given" do allow(box_collection).to receive(:all) .and_return([ ["foo", "1.0", :virtualbox], ["foo", "1.1", :virtualbox], ]) env[:box_name] = "foo" env[:box_version] = "1.0" expect(box_collection).to receive(:find).with( "foo", :virtualbox, "1.0", nil).and_return(box) expect(box_collection).to receive(:clean).with(box.name) .and_return(false) expect(box).to receive(:destroy!).once expect(app).to receive(:call).with(env).once subject.call(env) expect(env[:box_removed]).to equal(box) end context "with architecture" do let(:architecture) { "test-arch" } let(:box) do box_dir = iso_env.box3("foo", "1.0", :virtualbox, architecture: architecture) Vagrant::Box.new("foo", :virtualbox, "1.0", box_dir, architecture: architecture) end it "deletes the box if it is the only option" do allow(box_collection).to receive(:all).and_return([["foo", "1.0", :virtualbox, architecture]]) env[:box_name] = "foo" expect(box_collection).to receive(:find). with("foo", :virtualbox, "1.0", architecture). and_return(box) expect(box_collection).to receive(:clean). with(box.name). and_return(true) expect(box).to receive(:destroy!).once expect(app).to receive(:call).with(env).once subject.call(env) expect(env[:box_removed]).to equal(box) end it "deletes the box with the specified provider if given" do allow(box_collection).to receive(:all) .and_return([ ["foo", "1.0", :virtualbox, architecture], ["foo", "1.0", :vmware, architecture], ]) env[:box_name] = "foo" env[:box_provider] = "virtualbox" expect(box_collection).to receive(:find).with( "foo", :virtualbox, "1.0", architecture).and_return(box) expect(box_collection).to receive(:clean).with(box.name) .and_return(false) expect(box).to receive(:destroy!).once expect(app).to receive(:call).with(env).once subject.call(env) expect(env[:box_removed]).to equal(box) end it "deletes the box with the specified version if given" do allow(box_collection).to receive(:all) .and_return([ ["foo", "1.0", :virtualbox, architecture], ["foo", "1.1", :virtualbox, architecture], ]) env[:box_name] = "foo" env[:box_version] = "1.0" expect(box_collection).to receive(:find).with( "foo", :virtualbox, "1.0", architecture).and_return(box) expect(box_collection).to receive(:clean).with(box.name) .and_return(false) expect(box).to receive(:destroy!).once expect(app).to receive(:call).with(env).once subject.call(env) expect(env[:box_removed]).to equal(box) end it "deletes the box with the specificed version and architecture" do allow(box_collection).to receive(:all) .and_return([ ["foo", "1.0", :virtualbox, architecture], ["foo", "1.0", :virtualbox, "other-arch"], ]) env[:box_name] = "foo" env[:box_version] = "1.0" env[:box_architecture] = architecture expect(box_collection).to receive(:find).with( "foo", :virtualbox, "1.0", architecture).and_return(box) expect(box_collection).to receive(:clean).with(box.name) .and_return(false) expect(box).to receive(:destroy!).once expect(app).to receive(:call).with(env).once subject.call(env) expect(env[:box_removed]).to equal(box) end it "errors when box with specified version does not included specified architecture" do allow(box_collection).to receive(:all) .and_return([ ["foo", "1.0", :virtualbox, architecture], ["foo", "1.0", :virtualbox, "other-arch"], ]) env[:box_name] = "foo" env[:box_version] = "1.0" env[:box_architecture] = "unknown-arch" expect { subject.call(env) }.to raise_error(Vagrant::Errors::BoxRemoveArchitectureNotFound) end it "errors when box with specified version has multiple architectures" do allow(box_collection).to receive(:all) .and_return([ ["foo", "1.0", :virtualbox, architecture], ["foo", "1.0", :virtualbox, "other-arch"], ]) env[:box_name] = "foo" env[:box_version] = "1.0" expect { subject.call(env) }.to raise_error(Vagrant::Errors::BoxRemoveMultiArchitecture) end end context "checking if a box is in use" do def new_entry(name, provider, version, valid=true) Vagrant::MachineIndex::Entry.new.tap do |entry| entry.extra_data["box"] = { "name" => "foo", "provider" => "virtualbox", "version" => "1.0", } allow(entry).to receive(:valid?).and_return(valid) end end let(:action_runner) { double("action_runner") } before do env[:action_runner] = action_runner allow(box_collection).to receive(:all) .and_return([ ["foo", "1.0", :virtualbox], ["foo", "1.1", :virtualbox], ]) env[:box_name] = "foo" env[:box_version] = "1.0" end it "does delete if the box is not in use" do expect(box_collection).to receive(:find).with( "foo", :virtualbox, "1.0", nil).and_return(box) expect(box).to receive(:destroy!).once expect(box_collection).to receive(:clean).with(box.name) .and_return(true) subject.call(env) end it "does delete if the box is in use and user confirms" do machine_index << new_entry("foo", "virtualbox", "1.0") result = { result: true } expect(action_runner).to receive(:run). with(anything, env).and_return(result) expect(box_collection).to receive(:find).with( "foo", :virtualbox, "1.0", nil).and_return(box) expect(box_collection).to receive(:clean).with(box.name) .and_return(true) expect(box).to receive(:destroy!).once subject.call(env) end it "doesn't delete if the box is in use" do machine_index << new_entry("foo", "virtualbox", "1.0") result = { result: false } expect(action_runner).to receive(:run). with(anything, env).and_return(result) expect(box_collection).to receive(:find).with( "foo", :virtualbox, "1.0", nil).and_return(box) expect(box).to receive(:destroy!).never subject.call(env) end it "doesn't delete if the box is in use and keep_used_boxes is set" do env[:keep_used_boxes] = true machine_index << new_entry("foo", "virtualbox", "1.0") result = { result: true } expect(action_runner).to receive(:run). with(anything, env).and_return(result) expect(box_collection).to receive(:find).with( "foo", :virtualbox, "1.0", nil).and_return(box) expect(box).to receive(:destroy!).never subject.call(env) end it "deletes if the box is in use and force is used" do machine_index << new_entry("foo", "virtualbox", "1.0") result = { result: true } expect(action_runner).to receive(:run). with(anything, env).and_return(result) expect(box_collection).to receive(:find).with( "foo", :virtualbox, "1.0", nil).and_return(box) expect(box_collection).to receive(:clean).with(box.name) .and_return(true) expect(box).to receive(:destroy!) subject.call(env) end end it "errors if the box doesn't exist" do allow(box_collection).to receive(:all).and_return([]) expect(app).to receive(:call).never expect { subject.call(env) }. to raise_error(Vagrant::Errors::BoxRemoveNotFound) end it "errors if the specified provider doesn't exist" do env[:box_name] = "foo" env[:box_provider] = "bar" allow(box_collection).to receive(:all).and_return([["foo", "1.0", :virtualbox]]) expect(app).to receive(:call).never expect { subject.call(env) }. to raise_error(Vagrant::Errors::BoxRemoveProviderNotFound) end it "errors if there are multiple providers" do env[:box_name] = "foo" allow(box_collection).to receive(:all) .and_return([ ["foo", "1.0", :virtualbox], ["foo", "1.0", :vmware], ]) expect(app).to receive(:call).never expect { subject.call(env) }. to raise_error(Vagrant::Errors::BoxRemoveMultiProvider) end it "errors if the specified provider has multiple versions" do env[:box_name] = "foo" env[:box_provider] = "virtualbox" allow(box_collection).to receive(:all) .and_return([ ["foo", "1.0", :virtualbox], ["foo", "1.1", :virtualbox], ]) expect(app).to receive(:call).never expect { subject.call(env) }. to raise_error(Vagrant::Errors::BoxRemoveMultiVersion) end it "errors if the specified version doesn't exist" do env[:box_name] = "foo" env[:box_version] = "1.1" allow(box_collection).to receive(:all).and_return([["foo", "1.0", :virtualbox]]) expect(app).to receive(:call).never expect { subject.call(env) }. to raise_error(Vagrant::Errors::BoxRemoveVersionNotFound) end end