From a37c49e9ece68de59bff06e16afa8bc59441a9f5 Mon Sep 17 00:00:00 2001 From: Moul <moul@moul.re> Date: Wed, 29 Jan 2020 20:39:52 +0100 Subject: [PATCH] [test] #262: Add verify blocks tests: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Integration test on the overall command: - Use click CliRunner().invoke() - https://click.palletsprojects.com/en/7.x/testing/#basic-testing - integration tests are requesting a node - If the node is down, the tests are broken - Unit tests on all the functions to test most of the major corner cases: - valid and a non-valid blocks signatures: - Kind of already in DuniterPy tests… --- tests/test_verify_blocks.py | 171 ++++++++++++++++++++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 tests/test_verify_blocks.py diff --git a/tests/test_verify_blocks.py b/tests/test_verify_blocks.py new file mode 100644 index 00000000..d7d6d3fd --- /dev/null +++ b/tests/test_verify_blocks.py @@ -0,0 +1,171 @@ +""" +Copyright 2016-2020 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 pytest +from click.testing import CliRunner + +from duniterpy.documents import Block +from duniterpy.api.client import Client +from duniterpy.api import bma + +from silkaj.network_tools import EndPoint +from silkaj.blocks import ( + verify_blocks_signatures, + check_passed_blocks_range, + get_chunk_size, + get_chunk, + verify_block_signature, + display_result, +) +from silkaj.constants import ( + SUCCESS_EXIT_STATUS, + FAILURE_EXIT_STATUS, + BMA_MAX_BLOCKS_CHUNK_SIZE, +) + + +G1_INVALID_BLOCK_SIG = 15144 +HEAD_BLOCK = 48000 + + +async def current(self): + return {"number": HEAD_BLOCK} + + +@pytest.mark.parametrize( + "from_block, to_block", [(2, 1), (20000, 15000), (0, HEAD_BLOCK + 1), (300000, 0)] +) +@pytest.mark.asyncio +async def test_check_passed_blocks_range(from_block, to_block, capsys, monkeypatch): + monkeypatch.setattr("duniterpy.api.bma.blockchain.current", current) + client = Client(EndPoint().BMA_ENDPOINT) + # https://medium.com/python-pandemonium/testing-sys-exit-with-pytest-10c6e5f7726f + with pytest.raises(SystemExit) as pytest_wrapped_e: + await check_passed_blocks_range(client, from_block, to_block) + assert pytest_wrapped_e.type == SystemExit + assert pytest_wrapped_e.value.code == FAILURE_EXIT_STATUS + captured = capsys.readouterr() + if to_block == HEAD_BLOCK + 1: + expected = "Passed TO_BLOCK argument is bigger than the head block: " + str( + HEAD_BLOCK + ) + else: + expected = "TO_BLOCK should be bigger or equal to FROM_BLOCK\n" + assert expected in captured.out + + +@pytest.mark.parametrize( + "from_block, to_block", + [ + (G1_INVALID_BLOCK_SIG, G1_INVALID_BLOCK_SIG), + (G1_INVALID_BLOCK_SIG - 5, G1_INVALID_BLOCK_SIG + 5), + (1, 10), + (HEAD_BLOCK - 1, 0), + ], +) +def test_verify_blocks_signatures(from_block, to_block, monkeypatch): + monkeypatch.setattr("duniterpy.api.bma.blockchain.current", current) + blocks_range = [str(from_block), str(to_block)] + result = CliRunner().invoke(verify_blocks_signatures, blocks_range) + assert result.exit_code == SUCCESS_EXIT_STATUS + if to_block == 0: + to_block = HEAD_BLOCK + expected = "Within {0}-{1} range, ".format(from_block, to_block) + if from_block == 1 or from_block == HEAD_BLOCK - 1: + expected += "no blocks with a wrong signature." + else: + expected += "blocks with a wrong signature: " + str(G1_INVALID_BLOCK_SIG) + assert expected + "\n" in result.output + + +@pytest.mark.parametrize( + "from_block, to_block, chunks_from, chunk_from", + [ + (140, 15150, [140, 5140, 10140, 15140], 140), + (140, 15150, [140, 5140, 10140, 15140], 15140), + (0, 2, [0], 0), + ], +) +def test_get_chunk_size(from_block, to_block, chunks_from, chunk_from): + chunk_size = get_chunk_size(from_block, to_block, chunks_from, chunk_from) + if chunk_from != chunks_from[-1]: + assert chunk_size == BMA_MAX_BLOCKS_CHUNK_SIZE + else: + assert chunk_size == to_block + 1 - chunk_from + + +@pytest.mark.parametrize("chunk_size, chunk_from", [(2, 1), (5, 10)]) +@pytest.mark.asyncio +async def test_get_chunks(chunk_size, chunk_from): + client = Client(EndPoint().BMA_ENDPOINT) + chunk = await get_chunk(client, chunk_size, chunk_from) + assert chunk[0]["number"] + chunk_size - 1 == chunk[-1]["number"] + await client.close() + + +invalid_signature = "fJusVDRJA8akPse/sv4uK8ekUuvTGj1OoKYVdMQQAACs7OawDfpsV6cEMPcXxrQTCTRMrTN/rRrl20hN5zC9DQ==" +invalid_block_raw = "Version: 10\nType: Block\nCurrency: g1\nNumber: 15144\nPoWMin: 80\n\ +Time: 1493683741\nMedianTime: 1493681008\nUnitBase: 0\n\ +Issuer: D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx\nIssuersFrame: 106\n\ +IssuersFrameVar: 0\nDifferentIssuersCount: 21\n\ +PreviousHash: 0000033D8562368F1B099E924A4A83119BDA0452FAB2A8A4F1B1BA11F5450597\n\ +PreviousIssuer: 5WD4WSHE96ySreSwQFXPqaKaKcwboRNApiPHjPWB6V9C\nMembersCount: 98\n\ +Identities:\nJoiners:\nActives:\nLeavers:\nRevoked:\nExcluded:\nCertifications:\n\ +Transactions:\nInnerHash: 8B194B5C38CF0A38D16256405AC3E5FA5C2ABD26BE4DCC0C7ED5CC9824E6155B\n\ +Nonce: 30400000119992\n" + +valid_signature = "qhXtFtl6A/ZL7JMb7guSDlxiISGsHkQ4hTz5mhhdZO0KCLqD2TmvjcGpUFETBSdRYVacvFYOvUANyevlcfx6Ag==" +valid_block_raw = "Version: 11\nType: Block\nCurrency: g1-test\nNumber: 509002\n\ +PoWMin: 60\nTime: 1580293955\nMedianTime: 1580292050\nUnitBase: 2\n\ +Issuer: 5B8iMAzq1dNmFe3ZxFTBQkqhq4fsztg1gZvxHXCk1XYH\nIssuersFrame: 26\n\ +IssuersFrameVar: 0\nDifferentIssuersCount: 5\n\ +PreviousHash: 0000EC4030E92E85F22F32663F5ABE137BA01CE59AF2A96050877320174C4A90\n\ +PreviousIssuer: Dz37iRAXeg4nUsfVH82m61b39HK5fqm6Bu7mM2ujLYz1\nMembersCount: 11\n\ +Identities:\nJoiners:\nActives:\nLeavers:\nRevoked:\nExcluded:\nCertifications:\n\ +Transactions:\nInnerHash: 19A53ABFA19EC77B6360E38EA98BE10154CB92307F4909AE49E786CA7149F8C6\n\ +Nonce: 10099900003511\n" + + +@pytest.mark.parametrize( + "signature, block_raw", + [(invalid_signature, invalid_block_raw), (valid_signature, valid_block_raw)], +) +def test_verify_block_signature(signature, block_raw): + # Check with valid and non-valid signatures block + invalid_signatures_blocks = [] + block = Block.from_signed_raw(block_raw + signature + "\n") + verify_block_signature(invalid_signatures_blocks, block) + if block.number == G1_INVALID_BLOCK_SIG: + assert invalid_signatures_blocks == [block.number] + else: + assert invalid_signatures_blocks == [] + + +@pytest.mark.parametrize( + "from_block, to_block, invalid_blocks_signatures", + [(0, 5, []), (100, 500, [53]), (470, 2341, [243, 453])], +) +def test_display_result(from_block, to_block, invalid_blocks_signatures, capsys): + expected = "Within {0}-{1} range, ".format(from_block, to_block) + if invalid_blocks_signatures: + expected += "blocks with a wrong signature: " + expected += " ".join(str(n) for n in invalid_blocks_signatures) + else: + expected += "no blocks with a wrong signature." + display_result(from_block, to_block, invalid_blocks_signatures) + captured = capsys.readouterr() + assert expected + "\n" == captured.out -- GitLab