require 'fileutils'
require_relative '../lib/hoosegow'

unless defined?(CONFIG)
  begin
    require_relative '../config'
  rescue LoadError
    CONFIG = {}
  end
end

inmate_dir = File.join(File.dirname(__FILE__), 'test_inmate')
CONFIG[:inmate_dir] = inmate_dir
CONFIG[:image_name] ||= Hoosegow.new(CONFIG).image_name

describe Hoosegow::Docker do
  context 'volumes' do
    subject { described_class.new(:volumes => volumes) }

    context 'unspecified' do
      subject { described_class.new }
      it { expect(subject.send(:volumes_for_bind)).to be_empty }
    end

    context 'empty' do
      let(:volumes) { {} }
      it { expect(subject.send(:volumes_for_bind)).to be_empty }
    end

    context 'with volumes' do
      let(:volumes) { {
        "/inside/path" => "/home/burke/data-for-container:rw",
        "/other/path" => "/etc/shared-config",
      } }
      it { expect(subject.send(:volumes_for_bind)).to eq([
        "/home/burke/data-for-container:/inside/path:rw",
        "/etc/shared-config:/other/path:ro",
      ]) }
    end
  end

  context "volume is writable" do
    it "calls after_create" do
      test_dir = File.join(Dir.pwd, 'volume-test')
      FileUtils.remove_dir(test_dir, true)
      FileUtils.mkpath(test_dir)

      config = CONFIG.merge(
        :Entrypoint => ['/usr/bin/touch',  '/volume-test/test'],
        :volumes => { '/volume-test' => test_dir + ":rw"}
      )
      docker = Hoosegow::Docker.new(config)
      begin
        docker.create_container CONFIG[:image_name]
        docker.start_container
      ensure
        docker.stop_container
        docker.delete_container
      end

      exists = File.exists?(File.join(test_dir, 'test'))
      expect(exists).to be_truthy
      FileUtils.remove_dir(test_dir, true)
    end
  end

  context "with custom connection options" do
    before(:each) {
      FileUtils.remove_dir(test_dir, true)
      FileUtils.mkpath(test_dir)
    }
    after { FileUtils.remove_dir(test_dir, true) }

    let(:test_dir) { File.join(Dir.pwd, 'volume-test') }

    it "configures ExtraHosts option" do
      config = CONFIG.merge(
        :Entrypoint => ['/bin/sh',  '-c', 'getent hosts some-service > /volume-test/out.txt'],
        :volumes => { '/volume-test' => test_dir + ":rw"},
        :HostConfig => {
          :ExtraHosts => ["some-service:127.0.0.1"]
        }
      )
      docker = Hoosegow::Docker.new(config)
      begin
        docker.create_container CONFIG[:image_name]
        docker.start_container
      ensure
        docker.stop_container
        docker.delete_container
      end

      # Ensure ExtraHosts config is set
      contents = File.read(File.join(test_dir, 'out.txt'))
      expect(contents).to match("127.0.0.1\s+some-service")
    end
  end


  context 'docker_url' do
    it "correctly generates TCP urls" do
      hoosegow = Hoosegow::Docker.new CONFIG.merge(:host => "1.1.1.1", :port => 1234)
      expect(::Docker.url).to eq("tcp://1.1.1.1:1234")
    end

    it "correctly generates Unix urls" do
      hoosegow = Hoosegow::Docker.new CONFIG.merge(:socket => "/path/to/socket")
      expect(::Docker.url).to eq("unix:///path/to/socket")
    end
  end

  context "callbacks" do
    let(:cb) { double("callback") }

    it "calls after_create" do
      expect(cb).to receive(:call).with(instance_of(Hash))
      docker = Hoosegow::Docker.new CONFIG.merge(:after_create => cb)
      begin
        docker.create_container CONFIG[:image_name]
      ensure
        docker.stop_container
        docker.delete_container
      end
    end

    it "calls after_start" do
      expect(cb).to receive(:call).with(instance_of(Hash))
      docker = Hoosegow::Docker.new CONFIG.merge(:after_start => cb)
      begin
        docker.create_container CONFIG[:image_name]
        docker.start_container
      ensure
        docker.stop_container
        docker.delete_container
      end
    end

    it "calls after_stop" do
      expect(cb).to receive(:call).with(instance_of(Hash))
      docker = Hoosegow::Docker.new CONFIG.merge(:after_stop => cb)
      begin
        docker.create_container CONFIG[:image_name]
        docker.start_container
      ensure
        docker.stop_container
        docker.delete_container
      end
    end
  end

  context "image_exist?" do
    it "returns true if the image exists" do
      docker = Hoosegow::Docker.new CONFIG
      expect(docker.image_exist?(CONFIG[:image_name])).to eq(true)
    end

    it "returns false if the image doesn't exist" do
      docker = Hoosegow::Docker.new CONFIG
      expect(docker.image_exist?("not_there")).to eq(false)
    end
  end

  context "delete_container" do
    let(:docker) { Hoosegow::Docker.new CONFIG }
    let(:container) { Object.new }
    before do
      docker.instance_variable_set(:@container, container)
      allow(container).to receive(:id).and_return("1234")
      $old_stderr = $stderr
      $stderr = StringIO.new
    end
    after do
      $stderr = $old_stderr
    end

    it "rescues error and prints when error is raised" do
      allow(container).to receive(:delete).
        and_raise(::Docker::Error::ServerError, "device or resource busy")
      docker.delete_container
      $stderr.rewind
      expect($stderr.read).to eql("Docker could not delete 1234: device or resource busy\n")
    end
  end
end
