"""
    :codeauthor: :email:`Gareth J. Greenaway <gareth@saltstack.com>`
    :codeauthor: :email:`David Murphy <dmurphy@saltstack.com>`
"""

import datetime
import logging
import pathlib
import shutil
import subprocess
import time
import types

import psutil
import pytest

import salt.modules.gpg as gpg
from tests.support.mock import MagicMock, call, patch

pytest.importorskip("gnupg")

pytestmark = [
    pytest.mark.skip_unless_on_linux,
    pytest.mark.requires_random_entropy,
    pytest.mark.slow_test,
]

log = logging.getLogger(__name__)


GPG_TEST_KEY_PASSPHRASE = "testkeypassphrase"
GPG_TEST_KEY_ID = "7416F045"
GPG_TEST_PUB_KEY = """-----BEGIN PGP PUBLIC KEY BLOCK-----

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
ylirh0aqUQdSeIuJTg==
=yF8M
-----END PGP PUBLIC KEY BLOCK-----
"""

GPG_TEST_PRIV_KEY = """-----BEGIN PGP PRIVATE KEY BLOCK-----


OZV2Hg+93dg3Wi6g/JW4OuTKWKuHRqpRB1J4i4lO
=WRTN
-----END PGP PRIVATE KEY BLOCK-----
"""


@pytest.fixture
def gpghome(tmp_path):
    root = tmp_path / "gpghome"
    root.mkdir(mode=0o0700)
    root.joinpath(".gnupg").mkdir(mode=0o0700)
    pub = root / "gpgfile.pub"
    pub.write_text(GPG_TEST_PUB_KEY)
    priv = root / "gpgfile.priv"
    priv.write_text(GPG_TEST_PRIV_KEY)
    try:
        yield types.SimpleNamespace(path=root, pub=pub, priv=priv)
    finally:
        # Make sure we don't leave any gpg-agent's running behind
        gpg_connect_agent = shutil.which("gpg-connect-agent")
        if gpg_connect_agent:
            gnupghome = root / ".gnupg"
            if not gnupghome.is_dir():
                gnupghome = root
            try:
                subprocess.run(
                    [gpg_connect_agent, "killagent", "/bye"],
                    env={"GNUPGHOME": str(gnupghome)},
                    shell=False,
                    check=True,
                    stdout=subprocess.DEVNULL,
                    stderr=subprocess.DEVNULL,
                )
            except subprocess.CalledProcessError:
                # This is likely CentOS 7 or Amazon Linux 2
                pass

        # If the above errored or was not enough, as a last resort, let's check
        # the running processes.
        for proc in psutil.process_iter():
            try:
                if "gpg-agent" in proc.name():
                    for arg in proc.cmdline():
                        if str(root) in arg:
                            proc.terminate()
            except Exception:  # pylint: disable=broad-except
                pass


@pytest.fixture
def configure_loader_modules(gpghome):
    return {gpg: {}}


def test_list_keys():
    """
    Test gpg.list_keys
    """

    _user_mock = {
        "shell": "/bin/bash",
        "workphone": "",
        "uid": 0,
        "passwd": "x",
        "roomnumber": "",
        "gid": 0,
        "groups": ["root"],
        "home": "/root",
        "fullname": "root",
        "homephone": "",
        "name": "root",
    }

    _list_result = [
        {
            "dummy": "",
            "keyid": "xxxxxxxxxxxxxxxx",
            "expires": "2011188692",
            "sigs": [],
            "subkeys": [
                ["xxxxxxxxxxxxxxxx", "e", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"]
            ],
            "length": "4096",
            "ownertrust": "-",
            "sig": "",
            "algo": "1",
            "fingerprint": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
            "date": "1506612692",
            "trust": "-",
            "type": "pub",
            "uids": ["GPG Person <person@example.com>"],
        },
        {
            "dummy": "",
            "keyid": "yyyyyyyyyyyyyyyy",
            "expires": "2011188692",
            "sigs": [],
            "subkeys": [
                [
                    "yyyyyyyyyyyyyyyy",
                    "e",
                    "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy",
                ]
            ],
            "length": "4096",
            "ownertrust": "-",
            "sig": "",
            "algo": "1",
            "fingerprint": "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy",
            "date": "1506612692",
            "trust": "r",
            "type": "pub",
            "uids": ["GPG Person <person@example.com>"],
        },
    ]

    _expected_result = [
        {
            "keyid": "xxxxxxxxxxxxxxxx",
            "uids": ["GPG Person <person@example.com>"],
            "created": "2017-09-28",
            "expires": "2033-09-24",
            "keyLength": "4096",
            "ownerTrust": "Unknown",
            "fingerprint": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
            "trust": "Unknown",
        },
        {
            "keyid": "yyyyyyyyyyyyyyyy",
            "uids": ["GPG Person <person@example.com>"],
            "created": "2017-09-28",
            "expires": "2033-09-24",
            "keyLength": "4096",
            "ownerTrust": "Unknown",
            "fingerprint": "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy",
            "trust": "Revoked",
        },
    ]

    mock_opt = MagicMock(return_value="root")
    with patch.dict(gpg.__salt__, {"user.info": MagicMock(return_value=_user_mock)}):
        with patch.dict(gpg.__salt__, {"config.option": mock_opt}):
            with patch.object(gpg, "_list_keys", return_value=_list_result):
                assert gpg.list_keys() == _expected_result


def test_get_key():
    """
    Test gpg.get_key
    """

    _user_mock = {
        "shell": "/bin/bash",
        "workphone": "",
        "uid": 0,
        "passwd": "x",
        "roomnumber": "",
        "gid": 0,
        "groups": ["root"],
        "home": "/root",
        "fullname": "root",
        "homephone": "",
        "name": "root",
    }

    _list_result = [
        {
            "dummy": "",
            "keyid": "xxxxxxxxxxxxxxxx",
            "expires": "2011188692",
            "sigs": [],
            "subkeys": [
                ["xxxxxxxxxxxxxxxx", "e", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"]
            ],
            "length": "4096",
            "ownertrust": "-",
            "sig": "",
            "algo": "1",
            "fingerprint": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
            "date": "1506612692",
            "trust": "-",
            "type": "pub",
            "uids": ["GPG Person <person@example.com>"],
        }
    ]

    _expected_result = {
        "fingerprint": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
        "keyid": "xxxxxxxxxxxxxxxx",
        "uids": ["GPG Person <person@example.com>"],
        "created": "2017-09-28",
        "trust": "Unknown",
        "ownerTrust": "Unknown",
        "expires": "2033-09-24",
        "keyLength": "4096",
    }

    mock_opt = MagicMock(return_value="root")
    with patch.dict(gpg.__salt__, {"user.info": MagicMock(return_value=_user_mock)}):
        with patch.dict(gpg.__salt__, {"config.option": mock_opt}):
            with patch.object(gpg, "_list_keys", return_value=_list_result):
                ret = gpg.get_key("xxxxxxxxxxxxxxxx")
                assert ret == _expected_result


def test_delete_key_without_passphrase(gpghome):
    """
    Test gpg.delete_key without passphrase
    """

    _user_mock = {
        "shell": "/bin/bash",
        "workphone": "",
        "uid": 0,
        "passwd": "x",
        "roomnumber": "",
        "gid": 0,
        "groups": ["root"],
        "home": str(gpghome.path),
        "fullname": "root",
        "homephone": "",
        "name": "root",
    }

    _list_result = [
        {
            "dummy": "",
            "keyid": "xxxxxxxxxxxxxxxx",
            "expires": "2011188692",
            "sigs": [],
            "subkeys": [
                ["xxxxxxxxxxxxxxxx", "e", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"]
            ],
            "length": "4096",
            "ownertrust": "-",
            "sig": "",
            "algo": "1",
            "fingerprint": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
            "date": "1506612692",
            "trust": "-",
            "type": "pub",
            "uids": ["GPG Person <person@example.com>"],
        }
    ]

    _expected_result = {
        "res": True,
        "message": (
            "Secret key for xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx deleted\nPublic"
            " key for xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx deleted"
        ),
    }

    mock_opt = MagicMock(return_value="root")
    with patch.dict(gpg.__salt__, {"user.info": MagicMock(return_value=_user_mock)}):
        with patch.dict(gpg.__salt__, {"config.option": mock_opt}):
            with patch.object(gpg, "_list_keys", return_value=_list_result):
                with patch(
                    "salt.modules.gpg.gnupg.GPG.delete_keys",
                    MagicMock(return_value="ok"),
                ) as gnupg_delete_keys:
                    ret = gpg.delete_key(
                        "xxxxxxxxxxxxxxxx", delete_secret=True, use_passphrase=False
                    )
                    assert ret == _expected_result
                    gnupg_delete_keys.assert_called_with(
                        "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
                        False,
                        expect_passphrase=False,
                    )


def test_delete_key_with_passphrase_without_gpg_passphrase_in_pillar(gpghome):
    """
    Test gpg.delete_key with passphrase but without gpg_passphrase pillar
    """

    _user_mock = {
        "shell": "/bin/bash",
        "workphone": "",
        "uid": 0,
        "passwd": "x",
        "roomnumber": "",
        "gid": 0,
        "groups": ["root"],
        "home": str(gpghome.path),
        "fullname": "root",
        "homephone": "",
        "name": "root",
    }

    _list_result = [
        {
            "dummy": "",
            "keyid": "xxxxxxxxxxxxxxxx",
            "expires": "2011188692",
            "sigs": [],
            "subkeys": [
                ["xxxxxxxxxxxxxxxx", "e", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"]
            ],
            "length": "4096",
            "ownertrust": "-",
            "sig": "",
            "algo": "1",
            "fingerprint": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
            "date": "1506612692",
            "trust": "-",
            "type": "pub",
            "uids": ["GPG Person <person@example.com>"],
        }
    ]

    _expected_result = {
        "res": True,
        "message": "gpg_passphrase not available in pillar.",
    }

    mock_opt = MagicMock(return_value="root")
    pillar_mock = MagicMock(return_value=None)
    with patch.dict(gpg.__salt__, {"user.info": MagicMock(return_value=_user_mock)}):
        with patch.dict(gpg.__salt__, {"config.option": mock_opt}), patch.dict(
            gpg.__salt__, {"pillar.get": pillar_mock}
        ):
            with patch.object(gpg, "_list_keys", return_value=_list_result):
                with patch(
                    "salt.modules.gpg.gnupg.GPG.delete_keys"
                ) as gnupg_delete_keys:
                    ret = gpg.delete_key("xxxxxxxxxxxxxxxx", delete_secret=True)
                    assert ret == _expected_result
                    gnupg_delete_keys.assert_not_called()


def test_delete_key_with_passphrase_with_gpg_passphrase_in_pillar(gpghome):
    """
    Test gpg.delete_key with passphrase and gpg_passphrase pillar
    """

    _user_mock = {
        "shell": "/bin/bash",
        "workphone": "",
        "uid": 0,
        "passwd": "x",
        "roomnumber": "",
        "gid": 0,
        "groups": ["root"],
        "home": str(gpghome.path),
        "fullname": "root",
        "homephone": "",
        "name": "root",
    }

    _list_result = [
        {
            "dummy": "",
            "keyid": "xxxxxxxxxxxxxxxx",
            "expires": "2011188692",
            "sigs": [],
            "subkeys": [
                ["xxxxxxxxxxxxxxxx", "e", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"]
            ],
            "length": "4096",
            "ownertrust": "-",
            "sig": "",
            "algo": "1",
            "fingerprint": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
            "date": "1506612692",
            "trust": "-",
            "type": "pub",
            "uids": ["GPG Person <person@example.com>"],
        }
    ]

    _expected_result = {
        "res": True,
        "message": (
            "Secret key for xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx deleted\nPublic"
            " key for xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx deleted"
        ),
    }

    mock_opt = MagicMock(return_value="root")
    pillar_mock = MagicMock(return_value=GPG_TEST_KEY_PASSPHRASE)
    with patch.dict(gpg.__salt__, {"user.info": MagicMock(return_value=_user_mock)}):
        with patch.dict(gpg.__salt__, {"config.option": mock_opt}), patch.dict(
            gpg.__salt__, {"pillar.get": pillar_mock}
        ):
            with patch.object(gpg, "_list_keys", return_value=_list_result):
                with patch(
                    "salt.modules.gpg.gnupg.GPG.delete_keys",
                    MagicMock(return_value="ok"),
                ) as gnupg_delete_keys:
                    ret = gpg.delete_key("xxxxxxxxxxxxxxxx", delete_secret=True)
                    assert ret == _expected_result
                    gnupg_delete_keys.assert_called_with(
                        "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
                        False,
                        passphrase=GPG_TEST_KEY_PASSPHRASE,
                    )


def test_export_public_key(gpghome):
    """
    Test gpg.export_key with single public key
    """

    _user_mock = {
        "shell": "/bin/bash",
        "workphone": "",
        "uid": 0,
        "passwd": "x",
        "roomnumber": "",
        "gid": 0,
        "groups": ["root"],
        "home": str(gpghome.path),
        "fullname": "root",
        "homephone": "",
        "name": "root",
    }

    mock_opt = MagicMock(return_value="root")
    with patch.dict(gpg.__salt__, {"user.info": MagicMock(return_value=_user_mock)}):
        with patch.dict(gpg.__salt__, {"config.option": mock_opt}):
            with patch(
                "salt.modules.gpg.gnupg.GPG.export_keys",
                MagicMock(return_value=GPG_TEST_PUB_KEY),
            ) as gnupg_export_keys:
                ret = gpg.export_key("xxxxxxxxxxxxxxxx", bare=True)
                assert ret == GPG_TEST_PUB_KEY
                gnupg_export_keys.assert_called_with(
                    ["xxxxxxxxxxxxxxxx"],
                    False,
                    expect_passphrase=False,
                )


def test_export_public_key_to_file(gpghome):
    """
    Test gpg.export_key output public key to file
    """

    _user_mock = {
        "shell": "/bin/bash",
        "workphone": "",
        "uid": 0,
        "passwd": "x",
        "roomnumber": "",
        "gid": 0,
        "groups": ["root"],
        "home": str(gpghome.path),
        "fullname": "root",
        "homephone": "",
        "name": "root",
    }

    exported_keyfile = gpghome.path / "exported_pub_key"
    mock_opt = MagicMock(return_value="root")
    pillar_mock = MagicMock(return_value=GPG_TEST_KEY_PASSPHRASE)

    with patch.dict(gpg.__salt__, {"user.info": MagicMock(return_value=_user_mock)}):
        with patch.dict(gpg.__salt__, {"config.option": mock_opt}):
            with patch(
                "salt.modules.gpg.gnupg.GPG.export_keys",
                MagicMock(return_value=GPG_TEST_PUB_KEY),
            ) as gnupg_export_keys:
                ret = gpg.export_key(
                    keyids="xxxxxxxxxxxxxxxx", output=exported_keyfile, bare=True
                )
                assert ret == GPG_TEST_PUB_KEY
                keyfile_contents = pathlib.Path(exported_keyfile).read_text()
                assert keyfile_contents == GPG_TEST_PUB_KEY


def test_export_multiple_public_keys(gpghome):
    """
    Test gpg.export_key with multiple public keys
    """

    _user_mock = {
        "shell": "/bin/bash",
        "workphone": "",
        "uid": 0,
        "passwd": "x",
        "roomnumber": "",
        "gid": 0,
        "groups": ["root"],
        "home": str(gpghome.path),
        "fullname": "root",
        "homephone": "",
        "name": "root",
    }

    mock_opt = MagicMock(return_value="root")
    with patch.dict(gpg.__salt__, {"user.info": MagicMock(return_value=_user_mock)}):
        with patch.dict(gpg.__salt__, {"config.option": mock_opt}):
            with patch(
                "salt.modules.gpg.gnupg.GPG.export_keys",
                MagicMock(return_value=GPG_TEST_PUB_KEY),
            ) as gnupg_export_keys:
                ret = gpg.export_key(
                    "xxxxxxxxxxxxxxxx,yyyyyyyyyyyyyyyy,zzzzzzzzzzzzzzzz",
                    bare=True,
                )
                assert ret == GPG_TEST_PUB_KEY
                gnupg_export_keys.assert_called_with(
                    ["xxxxxxxxxxxxxxxx", "yyyyyyyyyyyyyyyy", "zzzzzzzzzzzzzzzz"],
                    False,
                    expect_passphrase=False,
                )


def test_export_secret_key_with_gpg_passphrase_in_pillar(gpghome):
    """
    Test gpg.export_key with secret key and gpg_passphrase in pillar
    """

    _user_mock = {
        "shell": "/bin/bash",
        "workphone": "",
        "uid": 0,
        "passwd": "x",
        "roomnumber": "",
        "gid": 0,
        "groups": ["root"],
        "home": str(gpghome.path),
        "fullname": "root",
        "homephone": "",
        "name": "root",
    }

    mock_opt = MagicMock(return_value="root")
    pillar_mock = MagicMock(return_value=GPG_TEST_KEY_PASSPHRASE)
    with patch.dict(gpg.__salt__, {"user.info": MagicMock(return_value=_user_mock)}):
        with patch.dict(gpg.__salt__, {"config.option": mock_opt}), patch.dict(
            gpg.__salt__, {"pillar.get": pillar_mock}
        ):
            with patch(
                "salt.modules.gpg.gnupg.GPG.export_keys",
                MagicMock(return_value=GPG_TEST_PRIV_KEY),
            ) as gnupg_export_keys:
                ret = gpg.export_key(
                    "xxxxxxxxxxxxxxxx", secret=True, use_passphrase=True, bare=True
                )
                assert ret == GPG_TEST_PRIV_KEY
                gnupg_export_keys.assert_called_with(
                    ["xxxxxxxxxxxxxxxx"],
                    True,
                    passphrase=GPG_TEST_KEY_PASSPHRASE,
                )


def test_export_secret_key_to_file_with_gpg_passphrase_in_pillar(gpghome):
    """
    Test gpg.export_key output secret key to file with gpg_passphrase in pillar
    """

    _user_mock = {
        "shell": "/bin/bash",
        "workphone": "",
        "uid": 0,
        "passwd": "x",
        "roomnumber": "",
        "gid": 0,
        "groups": ["root"],
        "home": str(gpghome.path),
        "fullname": "root",
        "homephone": "",
        "name": "root",
    }

    exported_keyfile = gpghome.path / "exported_priv_key"
    mock_opt = MagicMock(return_value="root")
    pillar_mock = MagicMock(return_value=GPG_TEST_KEY_PASSPHRASE)
    with patch.dict(gpg.__salt__, {"user.info": MagicMock(return_value=_user_mock)}):
        with patch.dict(gpg.__salt__, {"config.option": mock_opt}), patch.dict(
            gpg.__salt__, {"pillar.get": pillar_mock}
        ):
            with patch(
                "salt.modules.gpg.gnupg.GPG.export_keys",
                MagicMock(return_value=GPG_TEST_PRIV_KEY),
            ) as gnupg_export_keys:
                ret = gpg.export_key(
                    keyids="xxxxxxxxxxxxxxxx",
                    secret=True,
                    output=exported_keyfile,
                    use_passphrase=True,
                    bare=True,
                )
                assert ret == GPG_TEST_PRIV_KEY
                gnupg_export_keys.assert_called_with(
                    ["xxxxxxxxxxxxxxxx"],
                    True,
                    passphrase=GPG_TEST_KEY_PASSPHRASE,
                )
                keyfile_contents = pathlib.Path(exported_keyfile).read_text()
                assert keyfile_contents == GPG_TEST_PRIV_KEY


def test_create_key_without_passphrase(gpghome):
    """
    Test gpg.create_key without a passphrase
    """

    _user_mock = {
        "shell": "/bin/bash",
        "workphone": "",
        "uid": 0,
        "passwd": "x",
        "roomnumber": "",
        "gid": 0,
        "groups": ["root"],
        "home": str(gpghome.path),
        "fullname": "root",
        "homephone": "",
        "name": "root",
    }

    mock_opt = MagicMock(return_value="root")
    with patch.dict(gpg.__salt__, {"user.info": MagicMock(return_value=_user_mock)}):
        with patch.dict(gpg.__salt__, {"config.option": mock_opt}):
            ret = gpg.create_key()
            assert ret["res"]
            assert ret["fingerprint"]
            assert ret["message"] == "GPG key pair successfully generated."


def test_create_key_with_passphrase_without_gpg_passphrase_in_pillar(gpghome):
    """
    Test gpg.create_key with passphrase but without gpg_passphrase pillar
    """

    _user_mock = {
        "shell": "/bin/bash",
        "workphone": "",
        "uid": 0,
        "passwd": "x",
        "roomnumber": "",
        "gid": 0,
        "groups": ["root"],
        "home": str(gpghome.path),
        "fullname": "root",
        "homephone": "",
        "name": "root",
    }

    mock_opt = MagicMock(return_value="root")
    pillar_mock = MagicMock(return_value=None)
    with patch.dict(gpg.__salt__, {"user.info": MagicMock(return_value=_user_mock)}):
        with patch.dict(gpg.__salt__, {"config.option": mock_opt}), patch.dict(
            gpg.__salt__, {"pillar.get": pillar_mock}
        ):
            ret = gpg.create_key(use_passphrase=True)
            assert not ret["res"]
            assert ret["message"] == "gpg_passphrase not available in pillar."


def test_create_key_with_passphrase_with_gpg_passphrase_in_pillar(gpghome):
    """
    Test gpg.create_key with passphrase and gpg_passphrase pillar
    """

    _user_mock = {
        "shell": "/bin/bash",
        "workphone": "",
        "uid": 0,
        "passwd": "x",
        "roomnumber": "",
        "gid": 0,
        "groups": ["root"],
        "home": str(gpghome.path),
        "fullname": "root",
        "homephone": "",
        "name": "root",
    }

    mock_opt = MagicMock(return_value="root")
    pillar_mock = MagicMock(return_value=GPG_TEST_KEY_PASSPHRASE)
    with patch.dict(gpg.__salt__, {"user.info": MagicMock(return_value=_user_mock)}):
        with patch.dict(gpg.__salt__, {"config.option": mock_opt}), patch.dict(
            gpg.__salt__, {"pillar.get": pillar_mock}
        ):
            ret = gpg.create_key(use_passphrase=True)
            assert ret["res"]
            assert ret["fingerprint"]
            assert ret["message"] == "GPG key pair successfully generated."


def test_search_keys(gpghome):
    """
    Test gpg.search_keys
    """

    _user_mock = {
        "shell": "/bin/bash",
        "workphone": "",
        "uid": 0,
        "passwd": "x",
        "roomnumber": "",
        "gid": 0,
        "groups": ["root"],
        "home": str(gpghome.path),
        "fullname": "root",
        "homephone": "",
        "name": "root",
    }

    _search_result = [
        {
            "keyid": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
            "uids": ["GPG Person <person@example.com>"],
            "expires": "",
            "sigs": [],
            "length": "1024",
            "algo": "17",
            "date": int(time.mktime(datetime.datetime(2004, 11, 13).timetuple())),
            "type": "pub",
        }
    ]

    _expected_result = [
        {
            "uids": ["GPG Person <person@example.com>"],
            "created": "2004-11-13",
            "keyLength": "1024",
            "keyid": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
        }
    ]

    mock_search_keys = MagicMock(return_value=_search_result)
    mock_opt = MagicMock(return_value="root")
    with patch.dict(gpg.__salt__, {"user.info": MagicMock(return_value=_user_mock)}):
        with patch.dict(gpg.__salt__, {"config.option": mock_opt}):
            with patch.object(gpg, "_search_keys", mock_search_keys):
                ret = gpg.search_keys("person@example.com")
                assert ret == _expected_result

                assert (
                    call("person@example.com", "keys.openpgp.org", None)
                    in mock_search_keys.mock_calls
                )

                ret = gpg.search_keys("person@example.com", "keyserver.ubuntu.com")
                assert ret == _expected_result

                assert (
                    call("person@example.com", "keyserver.ubuntu.com", None)
                    in mock_search_keys.mock_calls
                )


def test_gpg_import_pub_key(gpghome):
    config_user = MagicMock(return_value="salt")
    user_info = MagicMock(
        return_value={"name": "salt", "home": str(gpghome.path), "uid": 1000}
    )
    with patch.dict(gpg.__salt__, {"config.option": config_user}):
        with patch.dict(gpg.__salt__, {"user.info": user_info}):
            ret = gpg.import_key(None, str(gpghome.pub), "salt", str(gpghome.path))
            assert ret["res"] is True


def test_gpg_import_priv_key(gpghome):
    config_user = MagicMock(return_value="salt")
    user_info = MagicMock(
        return_value={"name": "salt", "home": str(gpghome.path), "uid": 1000}
    )
    with patch.dict(gpg.__salt__, {"config.option": config_user}):
        with patch.dict(gpg.__salt__, {"user.info": user_info}):
            ret = gpg.import_key(None, str(gpghome.priv), "salt", str(gpghome.path))
            assert ret["res"] is True


def test_gpg_sign(gpghome):
    """
    Test gpg.sign
    """

    config_user = MagicMock(return_value="salt")
    user_info = MagicMock(
        return_value={"name": "salt", "home": str(gpghome.path), "uid": 1000}
    )
    pillar_mock = MagicMock(return_value=GPG_TEST_KEY_PASSPHRASE)
    with patch.dict(gpg.__salt__, {"config.option": config_user}):
        with patch.dict(gpg.__salt__, {"user.info": user_info}):
            with patch.dict(gpg.__salt__, {"pillar.get": pillar_mock}):
                key = gpg.import_key(None, str(gpghome.priv), "salt", str(gpghome.path))
                assert key["res"] is True
                gpg_text_input = "The quick brown fox jumped over the lazy dog"
                ret = gpg.sign(
                    keyid=GPG_TEST_KEY_ID,
                    text=gpg_text_input,
                    use_passphrase=True,
                    gnupghome=str(gpghome.path),
                )
                assert "-----BEGIN PGP SIGNATURE-----" in str(ret)


def test_gpg_encrypt_message(gpghome):
    """
    Test gpg.encrypt
    """

    config_user = MagicMock(return_value="salt")

    user_info = MagicMock(
        return_value={"name": "salt", "home": str(gpghome.path), "uid": 1000}
    )
    with patch.dict(gpg.__salt__, {"config.option": config_user}):
        with patch.dict(gpg.__salt__, {"user.info": user_info}):
            key = gpg.import_key(None, str(gpghome.priv), "salt", str(gpghome.path))
            assert key["res"] is True
            gpg_text_input = "The quick brown fox jumped over the lazy dog"
            ret = gpg.encrypt(
                recipients=GPG_TEST_KEY_ID,
                text=gpg_text_input,
                use_passphrase=True,
                always_trust=True,
                gnupghome=str(gpghome.path),
            )
            assert ret["res"] is True


def test_gpg_encrypt_and_sign_message_with_gpg_passphrase_in_pillar(gpghome):
    """
    Test gpg.encrypt sign message with passphrase and gpg_passphrase in pillar
    """

    config_user = MagicMock(return_value="salt")

    user_info = MagicMock(
        return_value={"name": "salt", "home": str(gpghome.path), "uid": 1000}
    )
    pillar_mock = MagicMock(return_value=GPG_TEST_KEY_PASSPHRASE)
    with patch.dict(gpg.__salt__, {"config.option": config_user}):
        with patch.dict(gpg.__salt__, {"user.info": user_info}):
            with patch.dict(gpg.__salt__, {"pillar.get": pillar_mock}):
                key = gpg.import_key(None, str(gpghome.priv), "salt", str(gpghome.path))
                assert key["res"] is True
                gpg_text_input = "The quick brown fox jumped over the lazy dog"
                ret = gpg.encrypt(
                    recipients=GPG_TEST_KEY_ID,
                    text=gpg_text_input,
                    sign=True,
                    use_passphrase=True,
                    always_trust=True,
                    gnupghome=str(gpghome.path),
                )
                assert ret["res"] is True


def test_gpg_decrypt_message_with_gpg_passphrase_in_pillar(gpghome):
    """
    Test gpg.decrypt with passphrase and gpg_passphrase in pillar
    """

    gpg_encrypted_message = """-----BEGIN PGP MESSAGE-----
    hQGMA7z9rKs9ZvTOAQwAnMbwchCm1VXOD+Ml0rnNrhDhsRm+6O96FOq5lWY0ntkj
    vnXeFOgUf0wzK4hkQT/Yo4/ZpDkV3iwwSIjesqNDS1U/KWfbe2pFeph6w9fHFnXf
    e8RJY8OGJHN8A9TlCIyKDVKGoXZEcSDZ4K0fNL/OxF3LxsSjJ894F+e2MCnoQZgq
    3uxQFYb9Gec8iPpBcd7s74J8cX1To9rBKRNSP73NRwVvKbPgB0NjzrLVPmqHGO7Q
    wFf9tZdGYmxV/AB9H0gCUITBCwnndissVqFpjUpVEIL1jOBus73oiL9Grn5QlZic
    wDOZyKDlWR15jiiDaWvq4ES6O0j2Oj/FodItJhTbzeXEFOxn7cNbW2kAAc08Hs1u
    zPI2LywaCSM/IOxrRI8n/ExXxtU0k48827AWTLYmhm+vDct7gQHEJ8Qu3/d5FsP/
    f1qWrtl+E7TpIIlUIgtPL596LYiwQLisqh1JeqqleSnBESIzgg7teolYdYED606z
    j9a1oTUHvSnKz6Q5Yh850mYBCBc/iEn0gJBgseGK6rXJF54f5duybkuTKrV+7hUd
    euy/+K0mnQWPS0G9NhCdQAaydNBZywOyQU6Yv1gPHapRrHOkhZq51sBKmZRGdqH/
    llX+Wd7pUetBV+vmZp75l8q9CNVpefA=
    =WXYF
    -----END PGP MESSAGE-----
    """

    config_user = MagicMock(return_value="salt")
    user_info = MagicMock(
        return_value={"name": "salt", "home": str(gpghome.path), "uid": 1000}
    )
    pillar_mock = MagicMock(return_value=GPG_TEST_KEY_PASSPHRASE)
    with patch.dict(gpg.__salt__, {"config.option": config_user}):
        with patch.dict(gpg.__salt__, {"user.info": user_info}):
            with patch.dict(gpg.__salt__, {"pillar.get": pillar_mock}):
                key = gpg.import_key(None, str(gpghome.priv), "salt", str(gpghome.path))
                assert key["res"] is True
                ret = gpg.decrypt(
                    text=gpg_encrypted_message,
                    use_passphrase=True,
                    gnupghome=str(gpghome.path),
                )
                assert ret["res"] is True
