Skip to content
Snippets Groups Projects
test_revocation.py 27.6 KiB
Newer Older
# Copyright  2016-2023 Maël Azimi <m.a@moul.re>
#
# Silkaj is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Silkaj is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with Silkaj. If not, see <https://www.gnu.org/licenses/>.

import urllib
from pathlib import Path
from unittest.mock import Mock

import pytest
import rich_click as click
from click.testing import CliRunner
from duniterpy.api import bma
from duniterpy.documents.revocation import Revocation

from silkaj import auth
from silkaj.blockchain import tools as bc_tools
from silkaj.cli import cli
from silkaj.constants import FAILURE_EXIT_STATUS, SUCCESS_EXIT_STATUS
from silkaj.network import client_instance
from silkaj.public_key import gen_pubkey_checksum
from silkaj.wot import idty_tools, revocation
from silkaj.wot import tools as w_tools
from tests.patched.auth import patched_auth_method
from tests.patched.blockchain_tools import patched_get_head_block_gtest
from tests.patched.idty_tools import idty1, idty2, idty_block, lookup_one, lookup_two
Moul's avatar
Moul committed
# Useful function


def display_dry_options(display, dry_run):
    if display:
        return ["--display"]
Moul's avatar
Moul committed
    if dry_run:
        return ["--dry-run"]
Moul's avatar
Moul committed
    return []
Moul's avatar
Moul committed
# Values
# idty1

REV_DOC = Revocation(
    version=10,
    currency="g1-test",
    identity=idty1,
)
REV_DOC.signature = "dTv6HHnyBsMXofOZFT21Y3gRzG/frseCaZFfpatpCWj\
YsNA8HPHjTibLUcJ3E9ZUgd0QUV7Bbu868xQE+j/yAg=="

REV_DOC_FALSE = Revocation(
    version=10,
    currency="g1-test",
    identity=idty1,
)
REV_DOC_FALSE.signature = "XXXXXXXXBsMXofOZFT21Y3gRzG/frseCaZFfp\
atpCWjYsNA8HPHjTibLUcJ3E9ZUgd0QUV7Bbu868xQE+j/yAg=="


# idty2

REV_2 = Revocation(
    version=10,
    currency="g1-test",
    identity=idty2,
)
REV_2.signature = "42D2vbIJnv2aGqUMbD+BF+eChzzGo4R3CVPAl5hpIGvoT\
cZQCfKBsRRlZDx6Gwn6lsJ3KLiIwPQeJKGYCW2YBg=="

REV_2_FALSE = Revocation(
    version=10,
    currency="g1-test",
    identity=idty2,
)
REV_2_FALSE.signature = "XXXXXXIJnv2aGqUMbD+BF+eChzzGo4R3CVPAl5hp\
IGvoTcZQCfKBsRRlZDx6Gwn6lsJ3KLiIwPQeJKGYCW2YBg=="


WRONG_FORMAT_REV = "ersion: 10\
Type: Revocation\
Currency: g1-test\
Issuer: 969qRJs8KhsnkyzqarpL4RKZGMdVKNbZgu8fhsigM7Lj\
IdtyUniqueID: aa_aa\
IdtyTimestamp: 703902-00002D6BC5E4FC540A4E188C3880A0ACCA06CD77017D26231A515312162B4070\
IdtySignature: 3RNQcKNI1VMmuCpK7wer8haOA959EQSDIR1v0U\
e/7TpTCOmsU2zYCpC+tqgLQFxDX4A79sB61c11J5C/3Z/TCw==\
42D2vbIJnv2aGqUMbD+BF+eChzzGo4R3CVPAl5hpIGvoTcZQCfKBsRRlZDx6Gwn6lsJ3KLiIwPQeJKGYCW2YBg=="


ERROR_CODE = 1005
ERROR_MESSAGE = "Document has unkown fields or wrong line ending format"


Moul's avatar
Moul committed
# patched functions
def patch_get_id_block(node, number):
    return idty_block


def patched_auth_method_Claude():
    return patched_auth_method("a")


def patch_check_many_identities(idty):
    return True


def patched_choose_identity(pubkey):
    return (
        {
            "uid": idty1.uid,
            "meta": {"timestamp": str(idty1.block_id)},
            "self": "kFW2we2K3zx4PZODx0Wf+xdXAJTmYD+yqdyZBsPF7UwqdaCA4N+yHj7+09\
Gjsttl0i9GtWzodyJ6mBE1q7jcAw==",
        },
        idty1.pubkey,
        None,
    )


def patched_send_bma_revoke_error(wot_useless, rev_doc_useless):
    raise urllib.error.HTTPError(
        url="this/is/a/test.url",
        code=ERROR_CODE,
        msg=ERROR_MESSAGE,
        hdrs={},
        fp=None,
    )


Moul's avatar
Moul committed
# tests
Moul's avatar
Moul committed

# test cli dry-run
@pytest.mark.parametrize(
Moul's avatar
Moul committed
    ("subcommand", "expected_warn"),
    [
        (
            "publish",
            True,
        ),
        (
            "revoke",
            True,
        ),
    ],
)
def test_revocation_cli_dry_run(subcommand, expected_warn, monkeypatch):
    """
    Tests dry-run option behavior when associated with other options
    """
    monkeypatch.setattr(auth, "auth_method", patched_auth_method_Claude)
    monkeypatch.setattr(bc_tools, "get_head_block", patched_get_head_block_gtest)
    monkeypatch.setattr(w_tools, "choose_identity", patched_choose_identity)
    monkeypatch.setattr(bma.blockchain, "block", patch_get_id_block)
    monkeypatch.setattr(
Moul's avatar
Moul committed
        idty_tools,
        "check_many_identities",
        patch_check_many_identities,
    )

    print("subcommand: ", subcommand)  # debug

    warning = "WARNING: the document will only be displayed and will not be sent."

Moul's avatar
Moul committed
    command = ["--dry-run", "-gt", "wot", "revocation", subcommand]
    print("command: ", " ".join(command))  # debug
    file = "revocation.txt"
    runner = CliRunner()
    with runner.isolated_filesystem():
        Path(file).write_text(REV_DOC.signed_raw(), encoding="utf-8")
        result = runner.invoke(cli, args=command)
    assert idty1.pubkey in result.output
    assert "Version: 10" in result.output

    if expected_warn:
        assert warning in result.output
    else:
Moul's avatar
Moul committed
        assert warning not in result.output
CREATE_STRINGS = [
    idty1.pubkey,
    "Do you want to save the revocation document for this identity?",
]


@pytest.mark.parametrize(
    ("file", "user_input", "expected"),
            CREATE_STRINGS,
            CREATE_STRINGS,
            Path("test_doc"),
            CREATE_STRINGS,
            Path("test_doc"),
            CREATE_STRINGS,
            [*CREATE_STRINGS, "Ok, goodbye!"],
def test_revocation_cli_create(file, user_input, expected, monkeypatch):
    monkeypatch.setattr(auth, "auth_method", patched_auth_method_Claude)
    monkeypatch.setattr(bc_tools, "get_head_block", patched_get_head_block_gtest)
    monkeypatch.setattr(w_tools, "choose_identity", patched_choose_identity)
    monkeypatch.setattr(bma.blockchain, "block", patch_get_id_block)
    patched_save_doc = Mock()
    monkeypatch.setattr(
        revocation,
        "save_doc",
        patched_save_doc,
    )

    command = ["wot", "revocation", "create"]
        command.extend([str(file)])
    else:
        file = revocation.REVOCATION_LOCAL_PATH

    result = CliRunner().invoke(cli, args=command, input=user_input)
        patched_save_doc.assert_called_with(file, REV_DOC.signed_raw(), idty1.pubkey)
        patched_save_doc.assert_not_called()
    for expect in expected:
        assert expect in result.output


# test cli verify
@pytest.mark.parametrize(
    ("doc", "lookup", "file", "expected", "not_expected"),
    [
        (
            REV_DOC,
            lookup_one,
            "",
            [
                "| Public key |",
                "Revocation document is valid.\n",
            ],
        ),
        (
            REV_DOC,
            lookup_two,
            "",
            [
                "One matching identity!\nSimilar identities:",
                "uid",
                "| Public key |",
                "Revocation document is valid.\n",
            ],
            [],
        ),
        (
            REV_DOC_FALSE,
            lookup_one,
            "",
            ["Error: the signature of the revocation document is invalid."],
            [
                "| Public key |",
                "Revocation document is valid.\n",
            ],
        ),
        (
            REV_2,
            lookup_two,
            "test_doc",
            [
                "Revocation document does not match any valid identity.\nSimilar identities:",
                "uid",
                "Claude",
                "Claudia",
            ],
            [
                "Revocation document is valid.\n",
                "| Public key |",
            ],
        ),
    ],
)
def test_revocation_cli_verify(
Moul's avatar
Moul committed
    doc,
    lookup,
    file,
    expected,
    not_expected,
    monkeypatch,
):
    def patched_lookup(node, id_pubkey):
        return lookup

    monkeypatch.setattr(bma.wot, "lookup", patched_lookup)
    monkeypatch.setattr(bma.blockchain, "block", patch_get_id_block)
    monkeypatch.setattr(bc_tools, "get_head_block", patched_get_head_block_gtest)
    command = ["wot", "revocation", "verify"]
    if file:
        command.extend([file])
    else:
        file = revocation.REVOCATION_LOCAL_PATH

    # verify file
    runner = CliRunner()
    with runner.isolated_filesystem():
        Path(file).write_text(doc.signed_raw(), encoding="utf-8")
        result = runner.invoke(cli, args=command)
        for expect in expected:
            assert expect in result.output
        for not_expect in not_expected:
Moul's avatar
Moul committed
            assert not_expect not in result.output


# test cli publish
@pytest.mark.parametrize(
Moul's avatar
Moul committed
    ("display", "dry_run", "doc", "lookup", "file", "user_input", "expected"),
    [
        (
            False,
            False,
            REV_DOC,
            lookup_one,
            "",
            "yes\n",
            [
                "| Public key |",
                "Do you confirm sending this revocation document immediately?",
            ],
        ),
        (
            False,
            False,
            REV_DOC,
            lookup_two,
            "",
            "yes\n",
            [
                "One matching identity!\nSimilar identities:",
                "Claudia",
                "| Public key |",
                "Do you confirm sending this revocation document immediately?",
            ],
        ),
        (
            True,
            False,
            REV_DOC,
            lookup_one,
            "",
            "yes\n",
            [
                "| Public key |",
                "Do you confirm sending this revocation document immediately?",
                "Version: 10",
            ],
        ),
        (
            True,
            False,
            REV_DOC,
            lookup_two,
            "",
            "yes\n",
            [
                "One matching identity!\nSimilar identities:",
                "| Public key |",
                "Do you confirm sending this revocation document immediately?",
                "Version: 10",
            ],
        ),
        (
            False,
            True,
            REV_DOC,
            lookup_one,
            "",
            None,
            [
                "WARNING: the document will only be displayed and will not be sent.",
                "Version: 10",
            ],
        ),
        (
            False,
            True,
            REV_DOC,
            lookup_two,
            "",
            None,
            [
                "One matching identity!\nSimilar identities:",
                "WARNING: the document will only be displayed and will not be sent.",
                "Version: 10",
            ],
        ),
        (
            False,
            False,
            REV_DOC,
            lookup_one,
            "",
            "no\n",
            [
                "| Public key |",
                "Do you confirm sending this revocation document immediately?",
            ],
        ),
        (
            True,
            False,
            REV_DOC,
            lookup_one,
            "",
            "no\n",
            [
                "| Public key |",
                "Do you confirm sending this revocation document immediately?",
                "Version: 10",
            ],
        ),
        (
            False,
            False,
            REV_DOC_FALSE,
            lookup_one,
            "",
            None,
            ["Error: the signature of the revocation document is invalid."],
        ),
        (
            True,
            False,
            REV_DOC_FALSE,
            lookup_one,
            "",
            None,
            ["Error: the signature of the revocation document is invalid."],
        ),
        (
            False,
            True,
            REV_DOC_FALSE,
            lookup_one,
            "",
            None,
            [
                "WARNING: the document will only be displayed and will not be sent.",
                "Error: the signature of the revocation document is invalid.",
            ],
        ),
        (
            False,
            False,
            REV_DOC,
            lookup_one,
            "test_doc",
            "yes\n",
            [
                "| Public key |",
                "Do you confirm sending this revocation document immediately?",
            ],
        ),
        (
            True,
            False,
            REV_DOC,
            lookup_one,
            "test_doc",
            "yes\n",
            [
                "| Public key |",
                "Do you confirm sending this revocation document immediately?",
            ],
        ),
        (
            False,
            True,
            REV_DOC,
            lookup_one,
            "test_doc",
            "yes\n",
            [
                "WARNING: the document will only be displayed and will not be sent.",
                "Version: 10",
            ],
        ),
        (
            False,
            False,
            REV_DOC_FALSE,
            lookup_one,
            "test_doc",
            None,
            ["Error: the signature of the revocation document is invalid."],
        ),
        (
            True,
            False,
            REV_DOC_FALSE,
            lookup_one,
            "test_doc",
            None,
            ["Error: the signature of the revocation document is invalid."],
        ),
        (
            False,
            True,
            REV_DOC_FALSE,
            lookup_one,
            "test_doc",
            None,
            [
                "Error: the signature of the revocation document is invalid.",
                "WARNING: the document will only be displayed and will not be sent.",
            ],
        ),
        (
            False,
            False,
            REV_2,
            lookup_two,
            "",
            "",
            [
                "Revocation document does not match any valid identity.\nSimilar identities:",
            ],
        ),
        (
            False,
            False,
            REV_DOC,
            False,
            "",
            "",
            ["Revocation document does not match any valid identity."],
        ),
    ],
)
def test_revocation_cli_publish(
Moul's avatar
Moul committed
    display,
    dry_run,
    doc,
    lookup,
    file,
    user_input,
    expected,
    monkeypatch,
):
    def patched_lookup(node, id_pubkey):
Moul's avatar
Moul committed
        if not lookup:
            raise urllib.error.HTTPError(
                url="this/is/a/test.url",
                code=404,
                msg="(Test) Not Found",
                hdrs={},
                fp=None,
            )
        return lookup

    monkeypatch.setattr(bma.wot, "lookup", patched_lookup)
    monkeypatch.setattr(bc_tools, "get_head_block", patched_get_head_block_gtest)
    monkeypatch.setattr(bma.blockchain, "block", patch_get_id_block)

    patched_send_bma_revoke = Mock()
    monkeypatch.setattr(bma.wot, "revoke", patched_send_bma_revoke)

    # prepare command
    command = display_dry_options(display, dry_run)
Moul's avatar
Moul committed
    command.extend(["wot", "revocation", "publish"])
    if file:
        command.extend([file])
    else:
        file = revocation.REVOCATION_LOCAL_PATH

    # test publication
    runner = CliRunner()
    with runner.isolated_filesystem():
        Path(file).write_text(doc.signed_raw(), encoding="utf-8")
        result = runner.invoke(cli, args=command, input=user_input)
        if user_input == "yes\n" and not dry_run:
Moul's avatar
Moul committed
            patched_send_bma_revoke.assert_called_once_with(
Moul's avatar
Moul committed
                client_instance(),
                doc.signed_raw(),
        elif dry_run or user_input == "no\n":
Moul's avatar
Moul committed
            patched_send_bma_revoke.assert_not_called()
        for expect in expected:
            assert expect in result.output


# test cli publish send errors
@pytest.mark.parametrize(
Moul's avatar
Moul committed
    ("display", "file", "user_input", "expected"),
    [
        (
            False,
            "",
            "yes\n",
            [
                "| Public key |",
                "Do you confirm sending this revocation document immediately?",
                "Error while publishing revocation",
            ],
        ),
        (
            True,
            "",
            "yes\n",
            [
                "| Public key |",
                "Do you confirm sending this revocation document immediately?",
                "Version: 10",
                "Error while publishing revocation",
            ],
        ),
        (
            False,
            "test_doc",
            "yes\n",
            [
                "| Public key |",
                "Do you confirm sending this revocation document immediately?",
                "Error while publishing revocation",
            ],
        ),
        (
            True,
            "test_doc",
            "yes\n",
            [
                "| Public key |",
                "Do you confirm sending this revocation document immediately?",
                "Version: 10",
                "Error while publishing revocation",
            ],
        ),
    ],
)
def test_revocation_cli_publish_send_errors(
Moul's avatar
Moul committed
    display,
    file,
    user_input,
    expected,
    monkeypatch,
):
    def patched_lookup(node, id_pubkey):
        return lookup_one

    monkeypatch.setattr(bma.wot, "lookup", patched_lookup)
    monkeypatch.setattr(bc_tools, "get_head_block", patched_get_head_block_gtest)
    monkeypatch.setattr(bma.blockchain, "block", patch_get_id_block)
    monkeypatch.setattr(bma.wot, "revoke", patched_send_bma_revoke_error)

    # prepare command
    command = display_dry_options(display, False)
Moul's avatar
Moul committed
    command.extend(["wot", "revocation", "publish"])
    if file:
        command.extend([file])
    else:
        file = revocation.REVOCATION_LOCAL_PATH

    # test publication
    runner = CliRunner()
    with runner.isolated_filesystem():
        Path(file).write_text(REV_DOC.signed_raw(), encoding="utf-8")
        result = runner.invoke(cli, args=command, input=user_input)
        for expect in expected:
            assert expect in result.output
        assert result.exit_code == FAILURE_EXIT_STATUS


# test cli revoke
@pytest.mark.parametrize(
Moul's avatar
Moul committed
    ("display", "dry_run", "user_input", "doc", "expected"),
                idty1.pubkey,
                "Do you confirm sending this revocation document immediately?",
            ],
        ),
        (
            True,
            False,
            "yes\n",
            REV_DOC,
            [
                idty1.pubkey,
                "Do you confirm sending this revocation document immediately?",
                "Version: 10",
            ],
        ),
        (
            False,
            True,
            None,
            REV_DOC,
            [
                "WARNING: the document will only be displayed and will not be sent.",
                idty1.pubkey,
                "Version: 10",
            ],
        ),
        (
            False,
            False,
            "no\n",
            REV_DOC,
            [
                idty1.pubkey,
                "Do you confirm sending this revocation document immediately?",
            ],
        ),
        (
            True,
            False,
            "no\n",
            REV_DOC,
            [
                idty1.pubkey,
                "Do you confirm sending this revocation document immediately?",
                "Version: 10",
            ],
        ),
    ],
)
def test_revocation_cli_revoke(
Moul's avatar
Moul committed
    display,
    dry_run,
    user_input,
    doc,
    expected,
    monkeypatch,
):
    monkeypatch.setattr(auth, "auth_method", patched_auth_method_Claude)
    monkeypatch.setattr(bc_tools, "get_head_block", patched_get_head_block_gtest)
    monkeypatch.setattr(w_tools, "choose_identity", patched_choose_identity)
    monkeypatch.setattr(bma.blockchain, "block", patch_get_id_block)
    patched_send_bma_revoke = Mock()
    monkeypatch.setattr(bma.wot, "revoke", patched_send_bma_revoke)

    command = display_dry_options(display, dry_run)
Moul's avatar
Moul committed
    command.extend(["wot", "revocation", "revoke"])

    result = CliRunner().invoke(cli, args=command, input=user_input)
    for expect in expected:
        assert expect in result.output
    if not dry_run and user_input == "yes\n":
        patched_send_bma_revoke.assert_called_once_with(client, doc.signed_raw())
    if dry_run or user_input == "no\n":
        patched_send_bma_revoke.assert_not_called()


# test cli revoke errors
@pytest.mark.parametrize(
Moul's avatar
Moul committed
    ("display", "user_input", "doc", "expected"),
                idty1.pubkey,
                "Do you confirm sending this revocation document immediately?",
                "Error while publishing revocation",
            ],
        ),
        (
            True,
            "yes\n",
            REV_DOC,
            [
                idty1.pubkey,
                "Do you confirm sending this revocation document immediately?",
                "Version: 10",
                "Error while publishing revocation",
            ],
        ),
    ],
)
def test_revocation_cli_revoke_errors(display, user_input, doc, expected, monkeypatch):
    monkeypatch.setattr(auth, "auth_method", patched_auth_method_Claude)
    monkeypatch.setattr(bc_tools, "get_head_block", patched_get_head_block_gtest)
    monkeypatch.setattr(w_tools, "choose_identity", patched_choose_identity)
    monkeypatch.setattr(bma.blockchain, "block", patch_get_id_block)
    monkeypatch.setattr(bma.wot, "revoke", patched_send_bma_revoke_error)

    command = display_dry_options(display, False)
Moul's avatar
Moul committed
    command.extend(["wot", "revocation", "revoke"])

    result = CliRunner().invoke(cli, args=command, input=user_input)
    for expect in expected:
        assert expect in result.output
    assert result.exit_code == FAILURE_EXIT_STATUS


# test create_revocation_doc
Moul's avatar
Moul committed
@pytest.mark.parametrize(("idty", "lookup"), [(idty1, lookup_one)])
def test_create_revocation_doc(idty, lookup):
    test = revocation.create_revocation_doc(
        lookup["results"][0]["uids"][0],
        idty1.pubkey,
        "g1-test",
    )
    expected = Revocation(
        version=10,
        currency="g1-test",
        identity=idty,
    )
    assert test == expected


# test save_doc
@pytest.mark.parametrize(
Moul's avatar
Moul committed
    ("path", "rev_1", "rev_2", "pubkey"),
            Path("./test_doc.txt"),
            REV_DOC,
            REV_2,
            idty1.pubkey,
        ),
        (
            Path("revocation"),
            idty1.pubkey,
        ),
    ],
)
def test_save_doc(path, rev_1, rev_2, pubkey, capsys, monkeypatch):
    def conf_true(confirm_question):
        return True

    def conf_false(confirm_question):
        return False

    runner = CliRunner()
    with runner.isolated_filesystem():
        # test file is written on en empty location
        revocation.save_doc(path, rev_1.signed_raw(), pubkey)
        assert path.is_file()
        assert path.read_text(encoding="utf-8") == rev_1.signed_raw()
        # test file is overwritten if confirm
        monkeypatch.setattr(click, "confirm", value=conf_true)
        revocation.save_doc(path, rev_2.signed_raw(), pubkey)
        expected_confirm = f"Revocation document file stored into `{path}` \
for following public key: {gen_pubkey_checksum(pubkey)}"
        assert expected_confirm in capsys.readouterr().out
        assert path.read_text(encoding="utf-8") == rev_2.signed_raw()
        # test file is not overwritten if not confirm
        monkeypatch.setattr(click, "confirm", value=conf_false)
        with pytest.raises(SystemExit) as pytest_exit:
            revocation.save_doc(path, rev_1.signed_raw(), pubkey)
        assert pytest_exit.type == SystemExit
        assert pytest_exit.value.code == SUCCESS_EXIT_STATUS
        expected_confirm = "Ok, goodbye!"
        assert expected_confirm in capsys.readouterr().out
        assert path.read_text(encoding="utf-8") == rev_2.signed_raw()


# test verify_document
@pytest.mark.parametrize(
Moul's avatar
Moul committed
    ("doc", "lookup"),
    [
        (REV_DOC, lookup_one),
        (REV_DOC, lookup_two),
    ],
)
def test_verify_document(doc, lookup, capsys, monkeypatch):
    def patched_lookup(node, id_pubkey):
        return lookup

    # prepare test
    path = Path("test_file.txt")
    monkeypatch.setattr(bma.wot, "lookup", patched_lookup)

    # test
    runner = CliRunner()
    with runner.isolated_filesystem():
        path.write_text(doc.signed_raw(), encoding="utf-8")
        result = revocation.verify_document(path)
        display = capsys.readouterr().out
        if len(lookup["results"]) > 1:
            assert "One matching identity!\n" in display
            assert "Similar identities:" in display
        assert result == doc


# test verify_document: no matching identity
@pytest.mark.parametrize(
Moul's avatar
Moul committed
    ("doc", "lookup"),
    [
        (REV_2, lookup_one),
        (REV_2, lookup_two),
        (REV_2, False),
    ],
)
def test_verify_document_missing_id(doc, lookup, capsys, monkeypatch):
    def patched_lookup(node, id_pubkey):
Moul's avatar
Moul committed
        if not lookup:
            http_error = urllib.error.HTTPError(
                url="this.is/a/test/url",
                code=2001,
                msg="No matching identity",
                hdrs={},
                fp=None,
            )
            raise http_error
        return lookup

    # prepare test
    path = Path("test_file.txt")
    monkeypatch.setattr(bma.wot, "lookup", patched_lookup)

    # test
    runner = CliRunner()
    with runner.isolated_filesystem():
        path.write_text(doc.signed_raw(), encoding="utf-8")
        with pytest.raises(SystemExit) as pytest_exit:
Moul's avatar
Moul committed
            revocation.verify_document(path)
        assert pytest_exit.type == SystemExit
        display = capsys.readouterr().out
Moul's avatar
Moul committed
        if not lookup:
            assert "Revocation document does not match any valid identity." in str(
Moul's avatar
Moul committed
                pytest_exit.value.code,
            )
        else:
            assert pytest_exit.value.code == FAILURE_EXIT_STATUS
            assert (
                "Revocation document does not match any valid identity.\nSimilar identities:"
                in display
            )


# test verify_document : format and sign errors
@pytest.mark.parametrize(
Moul's avatar
Moul committed
    ("doc", "currency"),
    [
        (REV_DOC_FALSE, REV_DOC_FALSE.currency),
        (REV_2_FALSE, REV_2_FALSE.currency),
        (WRONG_FORMAT_REV, "g1-test"),
    ],
)
def test_verify_document_sign_errors(doc, currency, monkeypatch):
    # prepare test
    file = Path("test_file.txt")
    patched_lookup = Mock()
    monkeypatch.setattr(bma.wot, "lookup", patched_lookup)
    # test
    runner = CliRunner()
    with runner.isolated_filesystem():
        if isinstance(doc, str):
            file.write_text(doc, encoding="utf-8")
        elif isinstance(doc, Revocation):
            file.write_text(doc.signed_raw(), encoding="utf-8")
        with pytest.raises(SystemExit) as pytest_exit:
            revocation.verify_document(file)
        assert pytest_exit.type == SystemExit
        if isinstance(doc, str):
            assert (
                "is not a revocation document, or is not correctly formatted."
                in pytest_exit.value.code
            )
        elif isinstance(doc, Revocation):
            assert (
                pytest_exit.value.code
                == "Error: the signature of the revocation document is invalid."
            )