# -*- coding: utf-8 -*-
"""
Unit tests for the docker state
"""

# Import Python Libs
from __future__ import absolute_import, print_function, unicode_literals

# Import Salt Libs
import salt.modules.dockermod as docker_mod
import salt.states.docker_volume as docker_state

# Import Salt Testing Libs
from tests.support.mixins import LoaderModuleMockMixin
from tests.support.mock import Mock, patch
from tests.support.unit import TestCase


class DockerVolumeTestCase(TestCase, LoaderModuleMockMixin):
    """
    Test docker_volume states
    """

    def setup_loader_modules(self):
        return {
            docker_mod: {"__context__": {"docker.docker_version": ""}},
            docker_state: {"__opts__": {"test": False}},
        }

    def test_present(self):
        """
        Test docker_volume.present
        """
        volumes = []
        default_driver = "dummy_default"

        def create_volume(name, driver=None, driver_opts=None):
            for v in volumes:
                # present should never try to add a conflicting
                # volume
                self.assertNotEqual(v["Name"], name)
            if driver is None:
                driver = default_driver
            new = {"Name": name, "Driver": driver}
            volumes.append(new)
            return new

        def remove_volume(name):
            old_len = len(volumes)
            removed = [v for v in volumes if v["Name"] == name]
            # present should not have tried to remove a volume
            # that didn't exist
            self.assertEqual(1, len(removed))
            volumes.remove(removed[0])
            return removed[0]

        docker_create_volume = Mock(side_effect=create_volume)
        __salt__ = {
            "docker.create_volume": docker_create_volume,
            "docker.volumes": Mock(return_value={"Volumes": volumes}),
            "docker.remove_volume": Mock(side_effect=remove_volume),
        }
        with patch.dict(docker_state.__dict__, {"__salt__": __salt__}):
            ret = docker_state.present("volume_foo")
            docker_create_volume.assert_called_with(
                "volume_foo", driver=None, driver_opts=None
            )
            self.assertEqual(
                {
                    "name": "volume_foo",
                    "comment": "",
                    "changes": {
                        "created": {"Driver": default_driver, "Name": "volume_foo"},
                    },
                    "result": True,
                },
                ret,
            )
            self.assertEqual(len(volumes), 1)
            self.assertEqual(volumes[0]["Name"], "volume_foo")
            self.assertIs(volumes[0]["Driver"], default_driver)

            # run it again with the same arguments
            orig_volumes = [volumes[0].copy()]
            ret = docker_state.present("volume_foo")
            self.assertEqual(
                {
                    "name": "volume_foo",
                    "comment": "Volume 'volume_foo' already exists.",
                    "changes": {},
                    "result": True,
                },
                ret,
            )
            self.assertEqual(orig_volumes, volumes)

            # run it again with a different driver but don't force
            ret = docker_state.present("volume_foo", driver="local")
            self.assertEqual(
                {
                    "name": "volume_foo",
                    "comment": (
                        "Driver for existing volume 'volume_foo'"
                        " ('dummy_default') does not match specified"
                        " driver ('local') and force is False"
                    ),
                    "changes": {},
                    "result": False,
                },
                ret,
            )
            self.assertEqual(orig_volumes, volumes)

            # run it again with a different driver and force
            ret = docker_state.present("volume_foo", driver="local", force=True)
            self.assertEqual(
                {
                    "name": "volume_foo",
                    "comment": "",
                    "changes": {
                        "removed": {"Driver": default_driver, "Name": "volume_foo"},
                        "created": {"Driver": "local", "Name": "volume_foo"},
                    },
                    "result": True,
                },
                ret,
            )
            mod_orig_volumes = [orig_volumes[0].copy()]
            mod_orig_volumes[0]["Driver"] = "local"
            self.assertEqual(mod_orig_volumes, volumes)

    def test_present_with_another_driver(self):
        """
        Test docker_volume.present
        """
        docker_create_volume = Mock(return_value="created")
        docker_remove_volume = Mock(return_value="removed")
        __salt__ = {
            "docker.create_volume": docker_create_volume,
            "docker.remove_volume": docker_remove_volume,
            "docker.volumes": Mock(
                return_value={"Volumes": [{"Name": "volume_foo", "Driver": "foo"}]}
            ),
        }
        with patch.dict(docker_state.__dict__, {"__salt__": __salt__}):
            ret = docker_state.present("volume_foo", driver="bar", force=True,)
        docker_remove_volume.assert_called_with("volume_foo")
        docker_create_volume.assert_called_with(
            "volume_foo", driver="bar", driver_opts=None
        )
        self.assertEqual(
            ret,
            {
                "name": "volume_foo",
                "comment": "",
                "changes": {"created": "created", "removed": "removed"},
                "result": True,
            },
        )

    def test_present_wo_existing_volumes(self):
        """
        Test docker_volume.present without existing volumes.
        """
        docker_create_volume = Mock(return_value="created")
        docker_remove_volume = Mock(return_value="removed")
        __salt__ = {
            "docker.create_volume": docker_create_volume,
            "docker.remove_volume": docker_remove_volume,
            "docker.volumes": Mock(return_value={"Volumes": None}),
        }
        with patch.dict(docker_state.__dict__, {"__salt__": __salt__}):
            ret = docker_state.present("volume_foo", driver="bar", force=True,)
        docker_create_volume.assert_called_with(
            "volume_foo", driver="bar", driver_opts=None
        )
        self.assertEqual(
            ret,
            {
                "name": "volume_foo",
                "comment": "",
                "changes": {"created": "created"},
                "result": True,
            },
        )

    def test_absent(self):
        """
        Test docker_volume.absent
        """
        docker_remove_volume = Mock(return_value="removed")
        __salt__ = {
            "docker.remove_volume": docker_remove_volume,
            "docker.volumes": Mock(return_value={"Volumes": [{"Name": "volume_foo"}]}),
        }
        with patch.dict(docker_state.__dict__, {"__salt__": __salt__}):
            ret = docker_state.absent("volume_foo",)
        docker_remove_volume.assert_called_with("volume_foo")
        self.assertEqual(
            ret,
            {
                "name": "volume_foo",
                "comment": "",
                "changes": {"removed": "removed"},
                "result": True,
            },
        )
