# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: BUSL-1.1

require File.expand_path("../../../base", __FILE__)

describe Vagrant::Machine::Remote do
  include_context "unit"

  let(:klass) { Class.new(Vagrant::Machine).tap { |c| c.prepend(described_class) }}

  # == Machine Setup
  #
  # This setup is all copied from Vagrant::Machine unit tests - the only
  # difference is the above klass is used so we can prepend the remote stuff

  let(:name)     { "foo" }
  let(:ui) { double(:ui) }
  let(:provider) { new_provider_mock }
  let(:provider_cls) do
    obj = double("provider_cls")
    allow(obj).to receive(:new).with(anything(), anything()) {}.and_return(provider)
    obj
  end
  let(:provider_config) { Object.new }
  let(:provider_name) { :dummy }
  let(:provider_options) { {} }
  let(:base)     { false }
  let(:box) do
    double("box").tap do |b|
      allow(b).to receive(:name).and_return("foo")
      allow(b).to receive(:provider).and_return(:dummy)
      allow(b).to receive(:version).and_return("1.0")
    end
  end
  let(:config) do
    double("config").tap do |c|
      allow(c).to receive(:trigger).and_return( double(:trigger) )
    end
  end
  let(:vagrantfile) do
    double("vagrantfile").tap do |v|
      allow(v).to receive(:config).and_return( config )
    end
  end

  let(:config)   { env.vagrantfile.config }
  let(:data_dir) { Pathname.new(Dir.mktmpdir("vagrant-machine-data-dir")) }
  let(:env)      do
    # We need to create a Vagrantfile so that this test environment
    # has a proper root path
    test_env.vagrantfile("""
      Vagrant.configure('2') do |config|
        config.vm.define 'foo' do |c|
          c.vm.box = 'foo'
        end
      end
""")

    # Create the Vagrant::Environment instance
    test_env.create_vagrant_env
  end

  let(:test_env) { isolated_environment }

  let(:instance) { new_instance }

  after do
    FileUtils.rm_rf(data_dir) if data_dir
  end

  subject { instance }

  def new_provider_mock
    double("provider").tap do |obj|
      allow(obj).to receive(:_initialize)
        .with(provider_name, anything).and_return(nil)
      allow(obj).to receive(:machine_id_changed).and_return(nil)
      allow(obj).to receive(:state).and_return(Vagrant::MachineState.new(
        :created, "", ""))
    end
  end

  # Returns a new instance with the test data
  def new_instance
    klass.new(name, provider_name, provider_cls, provider_config,
                        provider_options, config, data_dir, box,
                        env, env.vagrantfile, base, client: client)
  end

  # == Machine::Remote Setup
  #
  # Now we do the setup that's specific to the remote module.
  let(:client) { double(:client) }


  before do
    allow(env).to receive(:ui).and_return(ui)
    allow(env).to receive(:get_target) { client }
    allow(client).to receive(:box) { box }
    allow(client).to receive(:data_dir) { data_dir }
    allow(client).to receive(:name) { name }
    allow(client).to receive(:provider_name) { provider_name }
    allow(client).to receive(:provider) { nil }
    allow(client).to receive(:environment) { env }
    allow(client).to receive(:vagrantfile) { vagrantfile }
    allow(ui).to receive(:machine)

    allow(Vagrant.plugin("2").remote_manager).to receive(:providers) { {provider_name => provider_cls} }
  end

  describe "#synced_folders" do
    it "gets the synced_folders from the client" do
      expect(client).to receive(:synced_folders) { [] }
      subject.synced_folders
    end

    it "returns a hash with synced_folders returned from the client" do
      synced_folder_clients = [
        {
          plugin: double("plugin", name: "syncedfoldertype"),
          folder: {
            disabled: false,
            source: "/some/source",
            destination: "/some/destination",
          }
        }
      ]
      allow(client).to receive(:synced_folders) { synced_folder_clients }

      output = subject.synced_folders

      expect(output).to match(
        syncedfoldertype: a_hash_including(
          "/some/destination" => a_hash_including(
            disabled: false,
            guestpath: "/some/destination",
            hostpath: "/some/source",
            plugin: an_instance_of(Vagrant::Plugin::Remote::SyncedFolder),
          )
        )
      )
    end

    it "works with multiple folders for a given impl" do
      synced_folder_clients = [
        {
          plugin: double("pluginone", name: "syncedfoldertype"),
          folder: {
            disabled: false,
            source: "/one",
            destination: "/first",
          }
        },
        {
          plugin: double("plugintwo", name: "syncedfoldertype"),
          folder: {
            disabled: false,
            source: "/two",
            destination: "/second",
          }
        },
      ]
      allow(client).to receive(:synced_folders) { synced_folder_clients }

      output = subject.synced_folders

      expect(output).to match(
        syncedfoldertype: a_hash_including(
          "/first" => a_hash_including(
            disabled: false,
            guestpath: "/first",
            hostpath: "/one",
            plugin: an_instance_of(Vagrant::Plugin::Remote::SyncedFolder),
          ),
          "/second" => a_hash_including(
            disabled: false,
            guestpath: "/second",
            hostpath: "/two",
            plugin: an_instance_of(Vagrant::Plugin::Remote::SyncedFolder),
          ),
        )
      )
    end

    it "skips disabled folders" do
      synced_folder_clients = [
        {
          plugin: double("pluginone"),
          folder: {
            disabled: true,
            source: "/notme",
            destination: "/noway",
          }
        },
        {
          plugin: double("plugintwo", name: "syncedfoldertype"),
          folder: {
            disabled: false,
            source: "/yesme",
            destination: "/pickme",
          }
        },
      ]
      allow(client).to receive(:synced_folders) { synced_folder_clients }

      output = subject.synced_folders

      expect(output).to match(
        syncedfoldertype: a_hash_including(
          "/pickme" => a_hash_including(
            disabled: false,
            guestpath: "/pickme",
            hostpath: "/yesme",
            plugin: an_instance_of(Vagrant::Plugin::Remote::SyncedFolder),
          ),
        )
      )
      expect(output[:syncedfoldertype]).to_not have_key("/noway")
    end

    it "honors explicitly set folder type" do
      synced_folder_clients = [
        {
          plugin: double("pluginone"),
          folder: {
            disabled: false,
            source: "/imspecial",
            destination: "/ihaveatype",
            type: "specialtype",
          }
        },
        {
          plugin: double("plugintwo", name: "syncedfoldertype"),
          folder: {
            disabled: false,
            source: "/imnormal",
            destination: "/iamdefaulttype",
          }
        },
      ]
      allow(client).to receive(:synced_folders) { synced_folder_clients }

      output = subject.synced_folders

      expect(output).to match(
        syncedfoldertype: a_hash_including(
          "/iamdefaulttype" => a_hash_including(
            disabled: false,
            guestpath: "/iamdefaulttype",
            hostpath: "/imnormal",
            plugin: an_instance_of(Vagrant::Plugin::Remote::SyncedFolder),
          ),
        ),
        specialtype: a_hash_including(
          "/ihaveatype" => a_hash_including(
            disabled: false,
            guestpath: "/ihaveatype",
            hostpath: "/imspecial",
            plugin: an_instance_of(Vagrant::Plugin::Remote::SyncedFolder),
          ),
        )
      )
    end
  end
end
