require 'unit_helper'

require 'vagrant-lxc/container'

describe Vagrant::LXC::Container do
  # Default subject and container name for specs
  let(:name) { nil }
  subject { described_class.new(name) }

  describe 'container name validation' do
    let(:unknown_container) { described_class.new('unknown') }
    let(:valid_container)   { described_class.new('valid') }
    let(:new_container)     { described_class.new(nil) }

    before do
      unknown_container.stub(lxc: 'valid')
      valid_container.stub(lxc: 'valid')
    end

    it 'raises a NotFound error if an unknown container name gets provided' do
      expect {
        unknown_container.validate!
      }.to raise_error(Vagrant::LXC::Container::NotFound)
    end

    it 'does not raise a NotFound error if a valid container name gets provided' do
      expect {
        valid_container.validate!
      }.to_not raise_error(Vagrant::LXC::Container::NotFound)
    end

    it 'does not raise a NotFound error if nil is provider as name' do
      expect {
        new_container.validate!
      }.to_not raise_error(Vagrant::LXC::Container::NotFound)
    end
  end

  describe 'lxc commands execution' do
    let(:args) { @args }

    before do
      subject.stub(:execute) { |*args| @args = args }
      subject.lxc :command, '--state', 'RUNNING'
    end

    it 'prepends sudo' do
      args[0].should == 'sudo'
    end

    it 'uses the first argument as lxc command suffix' do
      args[1].should == 'lxc-command'
    end

    it 'pass through remaining arguments' do
      args[2].should == '--state'
      args[3].should == 'RUNNING'
    end
  end

  describe 'guard for container state' do
    let(:name) { 'random-container-name' }

    before do
      subject.stub :lxc
      subject.wait_until :running
    end

    it 'runs lxc-wait with the machine id and upcased state' do
      subject.should have_received(:lxc).with(
        :wait,
        '--name', name,
        '--state', 'RUNNING'
      )
    end
  end

  describe 'creation' do
    let(:name)            { 'random-container-name' }
    let(:template_name)   { 'template-name' }
    let(:rootfs_cache)    { '/path/to/cache' }
    let(:public_key_path) { Vagrant.source_root.join('keys', 'vagrant.pub').expand_path.to_s }

    before do
      subject.stub(lxc: true)
      SecureRandom.stub(hex: name)
      subject.create 'template-name' => template_name, 'rootfs-cache-path' => rootfs_cache, 'template-opts' => { '--foo' => 'bar'}
    end

    it 'calls lxc-create with the right arguments' do
      subject.should have_received(:lxc).with(
        :create,
        '--template', template_name,
        '--name', name,
        '--',
        '--auth-key', public_key_path,
        '--cache', rootfs_cache,
        '--foo', 'bar'
      )
    end
  end

  describe 'destruction' do
    let(:name) { 'container-name' }

    before do
      subject.stub(lxc: true)
      subject.destroy
    end

    it 'calls lxc-destroy with the right arguments' do
      subject.should have_received(:lxc).with(
        :destroy,
        '--name', name,
      )
    end
  end

  describe 'start' do
    let(:config) { mock(:config, start_opts: ['a=1', 'b=2']) }
    let(:name)   { 'container-name' }

    before do
      subject.stub(lxc: true, wait_until: true)
      subject.start(config)
    end

    it 'calls lxc-start with the right arguments' do
      subject.should have_received(:lxc).with(
        :start,
        '-d',
        '--name', name,
        '-s', 'a=1',
        '-s', 'b=2'
      )
    end

    it 'waits for container state to be RUNNING' do
      subject.should have_received(:wait_until).with(:running)
    end
  end

  describe 'halt' do
    let(:name) { 'random-container-name' }

    before do
      subject.stub(lxc: true, wait_until: true)
      subject.halt
    end

    it 'calls lxc-shutdown with the right arguments' do
      subject.should have_received(:lxc).with(
        :shutdown,
        '--name', name
      )
    end

    it 'waits for container state to be STOPPED' do
      subject.should have_received(:wait_until).with(:stopped)
    end
  end

  describe 'state' do
    let(:name) { 'random-container-name' }

    before do
      subject.stub(lxc: "state: STOPPED\npid: 2")
    end

    it 'calls lxc-info with the right arguments' do
      subject.state
      subject.should have_received(:lxc).with(
        :info,
        '--name', name
      )
    end

    it 'maps the output of lxc-info status out to a symbol' do
      subject.state.should == :stopped
    end
  end

  describe 'dhcp ip' do
    let(:name)      { 'random-container-name' }
    let(:ip)        { "10.0.4.123" }
    let(:server_ip) { "10.0.4.1" }

    before do
      subject.stub(:raw) {
        mock(stdout: "#{ip}\n", exit_code: 0)
      }
    end

    it 'digs the container ip from lxc dns server' do
      subject.dhcp_ip(server_ip).should == ip
      subject.should have_received(:raw).with('dig', name, "@#{server_ip}", '+short')
    end
  end
end