# -*- coding: utf-8 -*-
"""
Test case for the vault execution module
"""

# Import python libs
from __future__ import absolute_import, print_function, unicode_literals

import salt.modules.vault as vault

# Import Salt libs
from salt.exceptions import CommandExecutionError
from tests.support.mixins import LoaderModuleMockMixin
from tests.support.mock import MagicMock, patch

# Import Salt Testing libs
from tests.support.unit import TestCase


class TestVaultModule(LoaderModuleMockMixin, TestCase):
    """
    Test case for the vault execution module
    """

    def setup_loader_modules(self):
        return {
            vault: {
                "__opts__": {
                    "vault": {
                        "url": "http://127.0.0.1",
                        "auth": {"token": "test", "method": "token"},
                    }
                },
                "__grains__": {"id": "test-minion"},
            }
        }

    def test_read_secret_v1(self):
        """
        Test salt.modules.vault.read_secret function
        """
        version = {"v2": False, "data": None, "metadata": None, "type": None}
        mock_version = MagicMock(return_value=version)
        mock_vault = MagicMock()
        mock_vault.return_value.status_code = 200
        mock_vault.return_value.json.return_value = {"data": {"key": "test"}}
        with patch.dict(
            vault.__utils__, {"vault.make_request": mock_vault}
        ), patch.dict(vault.__utils__, {"vault.is_v2": mock_version}):
            vault_return = vault.read_secret("/secret/my/secret")

        self.assertDictEqual(vault_return, {"key": "test"})

    def test_read_secret_v1_key(self):
        """
        Test salt.modules.vault.read_secret function specifying key
        """
        version = {"v2": False, "data": None, "metadata": None, "type": None}
        mock_version = MagicMock(return_value=version)
        mock_vault = MagicMock()
        mock_vault.return_value.status_code = 200
        mock_vault.return_value.json.return_value = {"data": {"key": "somevalue"}}
        with patch.dict(
            vault.__utils__, {"vault.make_request": mock_vault}
        ), patch.dict(vault.__utils__, {"vault.is_v2": mock_version}):
            vault_return = vault.read_secret("/secret/my/secret", "key")

        self.assertEqual(vault_return, "somevalue")

    def test_read_secret_v2(self):
        """
        Test salt.modules.vault.read_secret function for v2 of kv secret backend
        """
        # given path secrets/mysecret generate v2 output
        version = {
            "v2": True,
            "data": "secrets/data/mysecret",
            "metadata": "secrets/metadata/mysecret",
            "type": "kv",
        }
        mock_version = MagicMock(return_value=version)
        mock_vault = MagicMock()
        mock_vault.return_value.status_code = 200
        v2_return = {
            "data": {
                "data": {"akey": "avalue"},
                "metadata": {
                    "created_time": "2018-10-23T20:21:55.042755098Z",
                    "destroyed": False,
                    "version": 13,
                    "deletion_time": "",
                },
            }
        }

        mock_vault.return_value.json.return_value = v2_return
        with patch.dict(
            vault.__utils__, {"vault.make_request": mock_vault}
        ), patch.dict(vault.__utils__, {"vault.is_v2": mock_version}):
            # Validate metadata returned
            vault_return = vault.read_secret("/secret/my/secret", metadata=True)
            self.assertDictContainsSubset({"data": {"akey": "avalue"}}, vault_return)
            # Validate just data returned
            vault_return = vault.read_secret("/secret/my/secret")
            self.assertDictContainsSubset({"akey": "avalue"}, vault_return)

    def test_read_secret_v2_key(self):
        """
        Test salt.modules.vault.read_secret function for v2 of kv secret backend
        with specified key
        """
        # given path secrets/mysecret generate v2 output
        version = {
            "v2": True,
            "data": "secrets/data/mysecret",
            "metadata": "secrets/metadata/mysecret",
            "type": "kv",
        }
        mock_version = MagicMock(return_value=version)
        mock_vault = MagicMock()
        mock_vault.return_value.status_code = 200
        v2_return = {
            "data": {
                "data": {"akey": "avalue"},
                "metadata": {
                    "created_time": "2018-10-23T20:21:55.042755098Z",
                    "destroyed": False,
                    "version": 13,
                    "deletion_time": "",
                },
            }
        }

        mock_vault.return_value.json.return_value = v2_return
        with patch.dict(
            vault.__utils__, {"vault.make_request": mock_vault}
        ), patch.dict(vault.__utils__, {"vault.is_v2": mock_version}):
            vault_return = vault.read_secret("/secret/my/secret", "akey")

        self.assertEqual(vault_return, "avalue")


class VaultDefaultTestCase(TestCase, LoaderModuleMockMixin):
    """
    Test cases for the default argument in the vault module

    NOTE: This test class is crafted such that the vault.make_request call will
    always fail. If you want to add other unit tests, you should put them in a
    separate class.
    """

    def setup_loader_modules(self):
        return {
            vault: {
                "__grains__": {"id": "foo"},
                "__utils__": {
                    "vault.make_request": MagicMock(side_effect=Exception("FAILED")),
                    "vault.is_v2": MagicMock(
                        return_value={
                            "v2": True,
                            "data": "secrets/data/mysecret",
                            "metadata": "secrets/metadata/mysecret",
                            "type": "kv",
                        }
                    ),
                },
            },
        }

    def setUp(self):
        self.path = "foo/bar/"

    def test_read_secret_with_default(self):
        assert vault.read_secret(self.path, default="baz") == "baz"

    def test_read_secret_no_default(self):
        try:
            vault.read_secret(self.path)
        except CommandExecutionError:
            # This is expected
            pass
        else:
            raise Exception("Should have raised a CommandExecutionError")

    def test_list_secrets_with_default(self):
        assert vault.list_secrets(self.path, default=["baz"]) == ["baz"]

    def test_list_secrets_no_default(self):
        try:
            vault.list_secrets(self.path)
        except CommandExecutionError:
            # This is expected
            pass
        else:
            raise Exception("Should have raised a CommandExecutionError")
