From 311e2938c0a0882e730bd5c1fab6ba8ab457fc10 Mon Sep 17 00:00:00 2001 From: jeanluc Date: Sun, 11 Dec 2022 14:19:07 +0100 Subject: [PATCH] Backport more meaningful tests --- tests/pytests/functional/states/test_gpg.py | 150 ++++++++++++++++++++ tests/pytests/unit/modules/test_gpg.py | 12 -- tests/pytests/unit/states/test_gpg.py | 89 +----------- 3 files changed, 157 insertions(+), 94 deletions(-) create mode 100644 tests/pytests/functional/states/test_gpg.py diff --git a/tests/pytests/functional/states/test_gpg.py b/tests/pytests/functional/states/test_gpg.py new file mode 100644 index 00000000000..4809f790652 --- /dev/null +++ b/tests/pytests/functional/states/test_gpg.py @@ -0,0 +1,150 @@ +import shutil +import subprocess + +import psutil +import pytest + +try: + import gnupg as gnupglib + + HAS_GNUPG = True +except ImportError: + HAS_GNUPG = False + + +pytestmark = [ + pytest.mark.skipif(HAS_GNUPG is False, reason="Needs python-gnupg library"), + pytest.mark.skip_if_binaries_missing("gpg", reason="Needs gpg binary"), +] + + +@pytest.fixture +def gpghome(tmp_path): + root = tmp_path / "gpghome" + root.mkdir(mode=0o0700) + try: + yield root + finally: + # Make sure we don't leave any gpg-agents 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 gpg(loaders, states, gpghome): + try: + yield states.gpg + finally: + pass + + +@pytest.fixture +def key_a_fp(): + return "EF03765F59EE904930C8A781553A82A058C0C795" + + +@pytest.fixture +def key_a_pub(): + return """\ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mI0EY4fxHQEEAJvXEaaw+o/yZCwMOJbt5FQHbVMMDX/0YI8UdzsE5YCC4iKnoC3x +FwFdkevKj3qp+45iBGLLnalfXIcVGXJGACB+tPHgsfHaXSDQPSfmX6jbZ6pHosSm +v1tTixY+NTJzGL7hDLz2sAXTbYmTbXeE9ifWWk6NcIwZivUbhNRBM+KxABEBAAG0 +LUtleSBBIChHZW5lcmF0ZWQgYnkgU2FsdFN0YWNrKSA8a2V5YUBleGFtcGxlPojR +BBMBCAA7FiEE7wN2X1nukEkwyKeBVTqCoFjAx5UFAmOH8R0CGy8FCwkIBwICIgIG +FQoJCAsCBBYCAwECHgcCF4AACgkQVTqCoFjAx5XURAQAguOwI+49lG0Kby+Bsyv3 +of3GgxvhS1Qa7+ysj088az5GVt0pqVe3SbRVvn/jyC6yZvWuv94KdL3R7hCeEz2/ +JakCRJ4wxEsdeASE8t9H/oTqD0I5asMa9EMvn5ICEGeLsTeQb7OYYihTQj7HJLG6 +pDEmK8EhJDvV/9o0lnhm/9w= +=Wc0O +-----END PGP PUBLIC KEY BLOCK-----""" + + +@pytest.fixture +def gnupg(gpghome): + return gnupglib.GPG(gnupghome=str(gpghome)) + + +@pytest.fixture +def gnupg_keyring(gpghome, keyring): + return gnupglib.GPG(gnupghome=str(gpghome), keyring=keyring) + + +@pytest.fixture(params=["a"]) +def pubkeys_present(gnupg, request): + pubkeys = [request.getfixturevalue(f"key_{x}_pub") for x in request.param] + fingerprints = [request.getfixturevalue(f"key_{x}_fp") for x in request.param] + gnupg.import_keys("\n".join(pubkeys)) + present_keys = gnupg.list_keys() + for fp in fingerprints: + assert any(x["fingerprint"] == fp for x in present_keys) + yield + # cleanup is taken care of by gpghome and tmp_path + + +@pytest.mark.usefixtures("pubkeys_present") +def test_gpg_present_no_changes(gpghome, gpg, gnupg, key_a_fp): + assert gnupg.list_keys(keys=key_a_fp) + ret = gpg.present( + key_a_fp[-16:], trust="unknown", gnupghome=str(gpghome), keyserver="nonexistent" + ) + assert ret.result + assert not ret.changes + + +@pytest.mark.skip_unless_on_linux( + reason="Complains about deleting private keys first when they are absent" +) +@pytest.mark.usefixtures("pubkeys_present") +def test_gpg_absent(gpghome, gpg, gnupg, key_a_fp): + assert gnupg.list_keys(keys=key_a_fp) + ret = gpg.absent(key_a_fp[-16:], gnupghome=str(gpghome)) + assert ret.result + assert ret.changes + assert "deleted" in ret.changes + assert ret.changes["deleted"] + + +def test_gpg_absent_no_changes(gpghome, gpg, gnupg, key_a_fp): + assert not gnupg.list_keys(keys=key_a_fp) + ret = gpg.absent(key_a_fp[-16:], gnupghome=str(gpghome)) + assert ret.result + assert not ret.changes + + +@pytest.mark.usefixtures("pubkeys_present") +def test_gpg_absent_test_mode_no_changes(gpghome, gpg, gnupg, key_a_fp): + assert gnupg.list_keys(keys=key_a_fp) + ret = gpg.absent(key_a_fp[-16:], gnupghome=str(gpghome), test=True) + assert ret.result is None + assert ret.changes + assert "deleted" in ret.changes + assert ret.changes["deleted"] + assert gnupg.list_keys(keys=key_a_fp) diff --git a/tests/pytests/unit/modules/test_gpg.py b/tests/pytests/unit/modules/test_gpg.py index 0a259508161..a5d18639dde 100644 --- a/tests/pytests/unit/modules/test_gpg.py +++ b/tests/pytests/unit/modules/test_gpg.py @@ -1073,15 +1073,3 @@ def test_gpg_receive_keys_no_user_id(): res = gpg.receive_keys(keys="abc", user="abc") assert res["res"] is False assert "no user ID" in res["message"][0] - - -def test_gpg_delete_key_honors_gnupghome(): - with patch("salt.modules.gpg._create_gpg") as create: - with patch("salt.modules.gpg.get_key") as get_key: - gnupghome = "/pls_respect_me" - get_key.return_value = None - gpg.delete_key("foo", gnupghome=gnupghome) - create.assert_called_with(None, gnupghome) - get_key.assert_called_with( - keyid="foo", fingerprint=None, user=None, gnupghome=gnupghome - ) diff --git a/tests/pytests/unit/states/test_gpg.py b/tests/pytests/unit/states/test_gpg.py index 2df53fbee17..13ba97cd493 100644 --- a/tests/pytests/unit/states/test_gpg.py +++ b/tests/pytests/unit/states/test_gpg.py @@ -92,25 +92,6 @@ def gpg_receive(request): yield recv -@pytest.fixture() -def gpg_delete(request): - delete = Mock(spec="salt.modules.gpg.delete_key") - delete.return_value = getattr( - request, "param", {"res": True, "message": "Public key for A deleted"} - ) - with patch.dict(gpg.__salt__, {"gpg.delete_key": delete}): - yield delete - - -@pytest.mark.usefixtures("gpg_list_keys") -def test_gpg_present_no_changes(gpg_receive, gpg_trust): - ret = gpg.present("A") - assert ret["result"] - assert not ret["changes"] - gpg_receive.assert_not_called() - gpg_trust.assert_not_called() - - @pytest.mark.usefixtures("gpg_list_keys") @pytest.mark.parametrize( "gpg_trust,expected", @@ -122,8 +103,8 @@ def test_gpg_present_no_changes(gpg_receive, gpg_trust): ) def test_gpg_present_trust_change(gpg_receive, gpg_trust, expected): ret = gpg.present("A", trust="marginally") - assert ret["result"] == expected - assert bool(ret["changes"]) == expected + assert ret["result"] is expected + assert bool(ret["changes"]) is expected gpg_trust.assert_called_once() gpg_receive.assert_not_called() @@ -147,8 +128,8 @@ def test_gpg_present_trust_change(gpg_receive, gpg_trust, expected): ) def test_gpg_present_new_key(gpg_receive, gpg_trust, expected): ret = gpg.present("new") - assert ret["result"] == expected - assert bool(ret["changes"]) == expected + assert ret["result"] is expected + assert bool(ret["changes"]) is expected gpg_receive.assert_called_once() gpg_trust.assert_not_called() @@ -165,9 +146,9 @@ def test_gpg_present_new_key(gpg_receive, gpg_trust, expected): @pytest.mark.usefixtures("gpg_list_keys") def test_gpg_present_new_key_and_trust(gpg_receive, gpg_trust, expected): ret = gpg.present("new", trust="marginally") - assert ret["result"] == expected + assert ret["result"] is expected # the key is always marked as added - assert bool(ret["changes"]) + assert ret["changes"] gpg_receive.assert_called_once() gpg_trust.assert_called_once() @@ -180,60 +161,4 @@ def test_gpg_present_test_mode_no_changes(gpg_receive, gpg_trust, key, trust): gpg_receive.assert_not_called() gpg_trust.assert_not_called() assert ret["result"] is None - assert bool(ret["changes"]) - - -@pytest.mark.usefixtures("gpg_list_keys") -def test_gpg_absent_no_changes(gpg_delete): - ret = gpg.absent("nonexistent") - assert ret["result"] - assert not ret["changes"] - gpg_delete.assert_not_called() - - -@pytest.mark.usefixtures("gpg_list_keys") -@pytest.mark.parametrize( - "gpg_delete,expected", - [ - ({"res": True, "message": "Public key for A deleted"}, True), - ( - { - "res": False, - "message": "Secret key exists, delete first or pass delete_secret=True.", - }, - False, - ), - ], - indirect=["gpg_delete"], -) -@pytest.mark.usefixtures("gpg_list_keys") -def test_gpg_absent_delete_key(gpg_delete, expected): - ret = gpg.absent("A") - assert ret["result"] == expected - assert bool(ret["changes"]) == expected - gpg_delete.assert_called_once() - - -@pytest.mark.usefixtures("gpg_list_keys") -def test_gpg_absent_test_mode_no_changes(gpg_delete): - with patch.dict(gpg.__opts__, {"test": True}): - ret = gpg.absent("A") - gpg_delete.assert_not_called() - assert ret["result"] is None - assert bool(ret["changes"]) - - -def test_gpg_absent_list_keys_with_gnupghome_and_user(gpg_list_keys): - gnupghome = "/pls_respect_me" - user = "imthereaswell" - gpg.absent("nonexistent", gnupghome=gnupghome, user=user) - gpg_list_keys.assert_called_with(gnupghome=gnupghome, user=user) - - -@pytest.mark.usefixtures("gpg_list_keys") -def test_gpg_absent_delete_key_called_with_correct_kwargs(gpg_delete): - key = "A" - user = "hellothere" - gnupghome = "/pls_sir" - gpg.absent(key, user=user, gnupghome=gnupghome) - gpg_delete.assert_called_with(keyid=key, gnupghome=gnupghome, user=user) + assert ret["changes"]