# frozen_string_literal: true

require 'spec_helper'
require 'support/sharedcontext'
require 'support/libvirt_context'

require 'vagrant-libvirt/errors'
require 'vagrant-libvirt/util/byte_number'
require 'vagrant-libvirt/action/create_domain'

describe VagrantPlugins::ProviderLibvirt::Action::CreateDomain do
  subject { described_class.new(app, env) }

  include_context 'unit'
  include_context 'libvirt'

  let(:libvirt_client) { double('libvirt_client') }
  let(:servers) { double('servers') }
  let(:volumes) { double('volumes') }
  let(:domain_volume) { double('domain_volume') }

  let(:domain_xml) { File.read(File.join(File.dirname(__FILE__), File.basename(__FILE__, '.rb'), domain_xml_file)) }
  let(:storage_pool_xml) { File.read(File.join(File.dirname(__FILE__), File.basename(__FILE__, '.rb'), storage_pool_xml_file)) }
  let(:libvirt_storage_pool) { double('storage_pool') }

  describe '#call' do
    before do
      allow_any_instance_of(VagrantPlugins::ProviderLibvirt::Driver)
        .to receive(:connection).and_return(connection)
      allow(connection).to receive(:client).and_return(libvirt_client)

      allow(connection).to receive(:servers).and_return(servers)
      allow(connection).to receive(:volumes).and_return(volumes)
      allow(volumes).to receive(:all).and_return([domain_volume])
      allow(domain_volume).to receive(:pool_name).and_return('default')
      allow(domain_volume).to receive(:[]).with('name').and_return('vagrant-test_default.img')
      allow(domain_volume).to receive(:path).and_return('/var/lib/libvirt/images/vagrant-test_default.img')
      allow(machine).to receive_message_chain("box.name") { 'vagrant-libvirt/test' }

      allow(logger).to receive(:info)
      allow(logger).to receive(:debug)
      allow(ui).to receive(:info)

      env[:domain_name] = "vagrant-test_default"

      env[:box_volumes] = []
      env[:box_volumes].push({
        :path=>"/test/box.img",
        :name=>"test_vagrant_box_image_1.1.1_0.img", 
        :virtual_size=> ByteNumber.new(5),
      })
      # should be ignored for system session and used for user session
      allow(Process).to receive(:uid).and_return(9999)
      allow(Process).to receive(:gid).and_return(9999)
    end

    context 'connection => qemu:///system' do
      let(:domain_xml_file) { 'default_domain.xml' }

      context 'default pool' do
        it 'should execute correctly' do
          expect(servers).to receive(:create).with(xml: domain_xml).and_return(machine)
          expect(volumes).to_not receive(:create) # additional disks only

          expect(subject.call(env)).to be_nil
        end

        context 'with no box' do
          let(:storage_pool_xml_file) { 'default_system_storage_pool.xml' }
          let(:vagrantfile) do
            <<-EOF
            Vagrant.configure('2') do |config|
              config.vm.define :test
            end
            EOF
          end

          it 'should query for the storage pool path' do
            expect(libvirt_client).to receive(:lookup_storage_pool_by_name).and_return(libvirt_storage_pool)
            expect(libvirt_storage_pool).to receive(:xml_desc).and_return(storage_pool_xml)
            expect(servers).to receive(:create).and_return(machine)

            expect(subject.call(env)).to be_nil
          end
        end

        context 'additional disks' do
          let(:vagrantfile_providerconfig) do
            <<-EOF
            libvirt.storage :file, :size => '20G'
            EOF
          end

          context 'volume create failed' do
            it 'should raise an exception' do
              expect(volumes).to receive(:create).and_raise(Libvirt::Error)

              expect{ subject.call(env) }.to raise_error(VagrantPlugins::ProviderLibvirt::Errors::FogCreateDomainVolumeError)
            end
          end

          context 'volume create succeeded' do
            let(:domain_xml_file) { 'additional_disks_domain.xml' }

            it 'should complete' do
              expect(volumes).to receive(:create).with(
                hash_including(
                  :path        => "/var/lib/libvirt/images/vagrant-test_default-vdb.qcow2",
                  :owner       => 0,
                  :group       => 0,
                  :pool_name   => "default",
                )
              )
              expect(servers).to receive(:create).with(xml: domain_xml).and_return(machine)

              expect(subject.call(env)).to be_nil
            end
          end
        end
      end

      context 'no default pool' do
        let(:vagrantfile) do
          <<-EOF
          Vagrant.configure('2') do |config|
            config.vm.define :test
          end
          EOF
        end

        it 'should raise an exception' do
          expect(libvirt_client).to receive(:lookup_storage_pool_by_name).and_return(nil)

          expect{ subject.call(env) }.to raise_error(VagrantPlugins::ProviderLibvirt::Errors::NoStoragePool)
        end
      end
    end

    context 'connection => qemu:///session' do
      let(:vagrantfile_providerconfig) do
        <<-EOF
        libvirt.qemu_use_session = true
        EOF
      end

      context 'default pool' do
        it 'should execute correctly' do
          expect(servers).to receive(:create).and_return(machine)

          expect(subject.call(env)).to be_nil
        end

        context 'with no box' do
          let(:storage_pool_xml_file) { 'default_user_storage_pool.xml' }
          let(:vagrantfile) do
            <<-EOF
            Vagrant.configure('2') do |config|
              config.vm.define :test
              config.vm.provider :libvirt do |libvirt|
                #{vagrantfile_providerconfig}
              end
            end
            EOF
          end

          it 'should query for the storage pool path' do
            expect(libvirt_client).to receive(:lookup_storage_pool_by_name).and_return(libvirt_storage_pool)
            expect(libvirt_storage_pool).to receive(:xml_desc).and_return(storage_pool_xml)
            expect(servers).to receive(:create).and_return(machine)

            expect(subject.call(env)).to be_nil
          end
        end

        context 'additional disks' do
          let(:vagrantfile_providerconfig) do
            <<-EOF
            libvirt.qemu_use_session = true
            libvirt.storage :file, :size => '20G'
            EOF
          end

          context 'volume create succeeded' do
            it 'should complete' do
              expect(volumes).to receive(:create).with(
                hash_including(
                  :path        => "/var/lib/libvirt/images/vagrant-test_default-vdb.qcow2",
                  :owner       => 9999,
                  :group       => 9999,
                  :pool_name   => "default",
                )
              )
              expect(servers).to receive(:create).and_return(machine)

              expect(subject.call(env)).to be_nil
            end
          end
        end
      end
    end
  end
end
