import mock
import os
import unittest
from mock import MagicMock

from captain.config import Config
from captain.docker_node import ConfigBasedDockerNodeResolver, AWSDockerNodeResolver, DockerNodeResolverFactory

minimal_config = {
    'SLUG_RUNNER_COMMAND': 'foo',
    'SLUG_RUNNER_IMAGE': 'foo',
    'AWS_DOCKER_HOST_TAG_VALUE': 'foo',
    'DOCKER_PROXY_USERNAME': 'foo',
    'DOCKER_PROXY_PASSWORD': 'foo'
}


class TestConfigBasedDockerNodeResolver(unittest.TestCase):

    def test_it_should_return_the_docker_nodes(self):
        with mock.patch.dict(os.environ, minimal_config):
            # Given
            config = Config()
            config.docker_nodes = ['1.1.1.1', '2.2.2.2']

            # When
            under_test = ConfigBasedDockerNodeResolver(config)

            # Then
            self.assertEquals(config.docker_nodes, under_test.get_docker_nodes())

    def test_it_should_raise_an_exception_when_docker_nodes_are_not_provided(self):
        # Given
        with mock.patch.dict(os.environ, minimal_config):
            config = Config()
            config.docker_nodes = []

            # When
            try:
                ConfigBasedDockerNodeResolver(config)
                self.fail("Exception should have been thrown but was not.")
            # Then
            except Exception as e:
                self.assertEquals("docker_nodes provided was not provided but is mandatory.", e.message)


class TestAWSDockerNodeResolver(unittest.TestCase):

    def setUp(self):
        with mock.patch.dict(os.environ, minimal_config):
            self.config = Config()
            self.config.aws_docker_host_tag_name = 'role'
            self.config.aws_docker_host_tag_value = 'appservers'
            self.config.docker_proxy_username = "username"
            self.config.docker_proxy_password = "password"
            self.config.docker_nodes = ['1.1.1.1', '2.2.2.2']
            self.aws_host_resolver = MagicMock()
            self.under_test = AWSDockerNodeResolver(self.aws_host_resolver, self.config)
            self.aws_host_resolver.find_running_hosts_private_ip_by_tag = MagicMock(return_value=self.config.docker_nodes)

    def test_it_should_get_docker_hosts_from_aws(self):
        # When
        self.under_test.get_docker_nodes()

        # Then
        self.aws_host_resolver.find_running_hosts_private_ip_by_tag.assert_called_once_with(
            self.config.aws_docker_host_tag_name, self.config.aws_docker_host_tag_value)

    def test_it_should_transform_aws_hosts_to_docker_nodes(self):
        # Given
        expected_docker_nodes = ['https://username:password@1.1.1.1:9400', 'https://username:password@2.2.2.2:9400']

        # When
        docker_nodes = self.under_test.get_docker_nodes()

        # Then
        self.assertListEqual(expected_docker_nodes, docker_nodes)


class TestDockerNodeResolverFactory(unittest.TestCase):

    def test_it_should_return_a_config_based_resolver(self):
        # Given
        with mock.patch.dict(os.environ, minimal_config):
            config = Config()
            under_test = DockerNodeResolverFactory(config)
            config.aws_docker_host_tag_value = None
            config.docker_nodes = ['1.1.1.1']

            # When
            node_resolver = under_test.get_host_resolver()

            # Then
            self.assertIsInstance(node_resolver, ConfigBasedDockerNodeResolver)

    def test_it_should_return_an_aws_based_resolver(self):
        # Given
        with mock.patch.dict(os.environ, minimal_config):
            config = Config()
            under_test = DockerNodeResolverFactory(config)
            config.aws_docker_host_tag_name = 'role'
            config.aws_docker_host_tag_value = 'appservers'
            config.aws_call_interval_secs = 180
            ec2_client = MagicMock
            aws_host_resolver = MagicMock

            # When
            with mock.patch.object(under_test, '_DockerNodeResolverFactory__create_ec2_client', return_value=ec2_client) as create_ec2_client_method:
                with mock.patch.object(under_test, '_DockerNodeResolverFactory__create_aws_host_resolver', return_value=aws_host_resolver) as create_aws_host_resolver_method:
                    node_resolver = under_test.get_host_resolver()

                    # Then
                    self.assertIsInstance(node_resolver, AWSDockerNodeResolver)
                    self.assertEquals(node_resolver.aws_host_resolver, aws_host_resolver)
                    self.assertEquals(node_resolver.aws_docker_host_tag_name, config.aws_docker_host_tag_name)
                    self.assertEquals(node_resolver.aws_docker_host_tag_value, config.aws_docker_host_tag_value)
                    create_ec2_client_method.assert_called_once()
                    create_aws_host_resolver_method.assert_called_once_with(ec2_client,
                                                                            config)

    def test_ec2_client_has_default_user_agent(self):
        with mock.patch.dict(os.environ, minimal_config):
            under_test = DockerNodeResolverFactory(config=Config())
            node_resolver = under_test.get_host_resolver()
            self.assertEqual('Captain', node_resolver.aws_host_resolver.ec2_client._client_config.user_agent)

    def test_ec2_client_has_configurable_user_agent(self):
        environ = minimal_config.copy()
        environ.update({'USER_AGENT': 'foo'})
        with mock.patch.dict(os.environ, environ):
            under_test = DockerNodeResolverFactory(config=Config())
            node_resolver = under_test.get_host_resolver()
            self.assertEqual('foo', node_resolver.aws_host_resolver.ec2_client._client_config.user_agent)
