Skip to content
Snippets Groups Projects
test_membership.py 7.02 KiB
# Copyright  2016-2022 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/>.

from unittest.mock import Mock

import pendulum
import pytest
from click.testing import CliRunner
from duniterpy.api import bma
from duniterpy.documents import Membership, get_block_id
from duniterpy.key import SigningKey

from patched.blockchain_tools import (
    currency,
    fake_block_id,
    patched_block,
    patched_get_head_block,
    patched_params,
)
from patched.wot import (
    patched_wot_requirements_no_pending,
    patched_wot_requirements_one_pending,
)
from silkaj import auth, blockchain_tools, membership, tui, wot
from silkaj.blockchain_tools import get_blockchain_parameters
from silkaj.cli import cli
from silkaj.constants import DATE
from silkaj.network_tools import client_instance

# Values and patches
PUBKEY = "EA7Dsw39ShZg4SpURsrgMaMqrweJPUFPYHwZA8e92e3D"
identity_block_id = get_block_id(
    "0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855"
)
identity_uid = "toto"

membership_block_id = fake_block_id


def patched_auth_method():
    return SigningKey.from_credentials(identity_uid, identity_uid)


def patched_choose_identity(pubkey):
    return (
        {"uid": identity_uid, "meta": {"timestamp": identity_block_id}},
        PUBKEY,
        None,
    )


@pytest.mark.parametrize(
    "dry_run, display, confirmation",
    [
        (True, False, False),
        (False, True, False),
        (False, True, True),
        (False, False, True),
    ],
)
def test_membership_cmd(dry_run, display, confirmation, monkeypatch):
    # Monkeypatch and Mock
    monkeypatch.setattr(auth, "auth_method", patched_auth_method)
    monkeypatch.setattr(blockchain_tools, "get_head_block", patched_get_head_block)
    monkeypatch.setattr(wot, "choose_identity", patched_choose_identity)

    patched_display_confirmation_table = Mock()
    monkeypatch.setattr(
        membership,
        "display_confirmation_table",
        patched_display_confirmation_table,
    )
    if not dry_run and not display:
        patched_generate_membership_document = Mock()
        monkeypatch.setattr(
            membership,
            "generate_membership_document",
            patched_generate_membership_document,
        )

    # Run membership command
    command = []
    if dry_run:
        command += ["--dry-run"]
    if display:
        command += ["--display"]
    command += ["membership"]
    pass_license = "No\nYes\n"
    confirmations = pass_license + ("Yes" if confirmation else "No")
    result = CliRunner().invoke(cli, args=command, input=confirmations)

    # Assert functions are called
    patched_display_confirmation_table.assert_called_once_with(
        identity_uid,
        PUBKEY,
        identity_block_id,
    )
    if dry_run or display:
        assert "Type: Membership" in result.output
    else:
        #    signing_key = patched_auth_method()
        patched_generate_membership_document.assert_called_once()
    #   membership_block_id is different
    #   patched_generate_membership_document.assert_called_once_with(
    #       PUBKEY,
    #       membership_block_id,
    #       identity_uid,
    #       identity_block_id,
    #       currency,
    #       signing_key,
    #   )


@pytest.mark.parametrize(
    "patched_wot_requirements",
    [patched_wot_requirements_no_pending, patched_wot_requirements_one_pending],
)
def test_display_confirmation_table(patched_wot_requirements, monkeypatch, capsys):
    monkeypatch.setattr(bma.wot, "requirements", patched_wot_requirements)
    monkeypatch.setattr(bma.blockchain, "parameters", patched_params)
    monkeypatch.setattr(bma.blockchain, "block", patched_block)

    client = client_instance()
    identities_requirements = client(bma.wot.requirements, PUBKEY)
    for identity_requirements in identities_requirements["identities"]:
        if identity_requirements["uid"] == identity_uid:
            membership_expires = identity_requirements["membershipExpiresIn"]
            pending_expires = identity_requirements["membershipPendingExpiresIn"]
            pending_memberships = identity_requirements["pendingMemberships"]
            break

    table = []
    if membership_expires:
        table.append(
            [
                "Expiration date of current membership",
                pendulum.now().add(seconds=membership_expires).diff_for_humans(),
            ]
        )

    if pending_memberships:
        table.append(
            [
                "Number of pending membership(s) in the mempool",
                len(pending_memberships),
            ]
        )
        table.append(
            [
                "Pending membership documents will expire",
                pendulum.now().add(seconds=pending_expires).diff_for_humans(),
            ]
        )

    table.append(["User Identifier (UID)", identity_uid])
    table.append(["Public Key", tui.gen_pubkey_checksum(PUBKEY)])

    table.append(["Block Identity", str(identity_block_id)[:45] + ""])

    block = client(bma.blockchain.block, identity_block_id.number)
    table.append(
        [
            "Identity published",
            pendulum.from_timestamp(block["time"], tz="local").format(DATE),
        ],
    )

    params = get_blockchain_parameters()
    table.append(
        [
            "Expiration date of new membership",
            pendulum.now().add(seconds=params["msValidity"]).diff_for_humans(),
        ]
    )

    table.append(
        [
            "Expiration date of new membership from the mempool",
            pendulum.now().add(seconds=params["msPeriod"]).diff_for_humans(),
        ]
    )

    display_table = tui.Table()
    display_table.fill_rows(table)
    expected = display_table.draw() + "\n"

    membership.display_confirmation_table(identity_uid, PUBKEY, identity_block_id)
    captured = capsys.readouterr()
    assert expected == captured.out


def test_generate_membership_document():
    signing_key = patched_auth_method()
    generated_membership = membership.generate_membership_document(
        PUBKEY,
        membership_block_id,
        identity_uid,
        identity_block_id,
        currency=currency,
        key=signing_key,
    )
    expected = Membership(
        issuer=PUBKEY,
        membership_block_id=membership_block_id,
        uid=identity_uid,
        identity_block_id=identity_block_id,
        signing_key=signing_key,
        currency=currency,
    )
    # Direct equality check can be done without raw() once Membership.__eq__() is implemented
    assert expected.raw() == generated_membership.raw()