コード例 #1
0
class BroadcastAndStoreCommandTestCase(TestCase):
    # noinspection SpellCheckingInspection
    def setUp(self):
        super(BroadcastAndStoreCommandTestCase, self).setUp()

        self.adapter = MockAdapter()
        self.command = BroadcastAndStoreCommand(self.adapter)

        # Define a few valid values that we can reuse across tests.
        self.trytes1 = b'RBTC9D9DCDQAEASBYBCCKBFA'
        self.trytes2 =\
          b'CCPCBDVC9DTCEAKDXC9D9DEARCWCPCBDVCTCEAHDWCTCEAKDCDFD9DSCSA'

    def test_wireup(self):
        """
    Verify that the command is wired up correctly.
    """
        self.assertIsInstance(
            Iota(self.adapter).broadcastAndStore,
            BroadcastAndStoreCommand,
        )

    def test_happy_path(self):
        """
    Successful invocation of ``broadcastAndStore``.
    """
        self.adapter.seed_response(
            'broadcastTransactions', {
                'trytes': [
                    text_type(self.trytes1, 'ascii'),
                    text_type(self.trytes2, 'ascii'),
                ],
            })

        self.adapter.seed_response('storeTransactions', {})

        trytes = [
            TransactionTrytes(self.trytes1),
            TransactionTrytes(self.trytes2),
        ]

        response = self.command(trytes=trytes)

        self.assertDictEqual(response, {'trytes': trytes})
コード例 #2
0
  def test_routing(self):
    """
    Routing commands to different adapters.
    """
    default_adapter = MockAdapter()
    pow_adapter     = MockAdapter()

    wrapper = (
      RoutingWrapper(default_adapter)
        .add_route('attachToTangle', pow_adapter)
        .add_route('interruptAttachingToTangle', pow_adapter)
    )

    default_adapter.seed_response('getNodeInfo', {'id': 'default1'})
    pow_adapter.seed_response('attachToTangle', {'id': 'pow1'})
    pow_adapter.seed_response('interruptAttachingToTangle', {'id': 'pow2'})

    self.assertDictEqual(
      wrapper.send_request({'command': 'attachToTangle'}),
      {'id': 'pow1'},
    )

    self.assertDictEqual(
      wrapper.send_request({'command': 'interruptAttachingToTangle'}),
      {'id': 'pow2'},
    )

    # Any commands that aren't routed go to the default adapter.
    self.assertDictEqual(
      wrapper.send_request({'command': 'getNodeInfo'}),
      {'id': 'default1'},
    )
コード例 #3
0
class GetTipsCommandTestCase(TestCase):
    def setUp(self):
        super(GetTipsCommandTestCase, self).setUp()

        self.adapter = MockAdapter()

    def test_wireup(self):
        """
    Verify that the command is wired up correctly.
    """
        self.assertIsInstance(
            Iota(self.adapter).getTips,
            GetTipsCommand,
        )

    def test_type_coercion(self):
        """
    The result is coerced to the proper type.

    https://github.com/iotaledger/iota.lib.py/issues/130
    """
        # noinspection SpellCheckingInspection
        self.adapter.seed_response(
            'getTips', {
                'duration':
                42,
                'hashes': [
                    'TESTVALUE9DONTUSEINPRODUCTION99999ANSVWB'
                    'CZ9ABZYUK9YYXFRLROGMCMQHRARDQPNMHHZSZ9999',
                    'TESTVALUE9DONTUSEINPRODUCTION99999HCZURL'
                    'NFWEDRFCYHWTYGUEMJLJ9ZIJTFASAVSEAZJGA9999',
                ],
            })

        gt_response = Iota(self.adapter).get_tips()

        self.assertEqual(
            list(map(type, gt_response['hashes'])),
            [TransactionHash] * 2,
        )
コード例 #4
0
class GetLatestInclusionCommandTestCase(TestCase):
  # noinspection SpellCheckingInspection
  def setUp(self):
    super(GetLatestInclusionCommandTestCase, self).setUp()

    self.adapter = MockAdapter()
    self.command = GetLatestInclusionCommand(self.adapter)

    # Define some tryte sequences that we can re-use across tests.
    self.milestone =\
      TransactionHash(
        b'TESTVALUE9DONTUSEINPRODUCTION99999W9KDIH'
        b'BALAYAFCADIDU9HCXDKIXEYDNFRAKHN9IEIDZFWGJ'
      )

    self.hash1 =\
      TransactionHash(
        b'TESTVALUE9DONTUSEINPRODUCTION99999TBPDM9'
        b'ADFAWCKCSFUALFGETFIFG9UHIEFE9AYESEHDUBDDF'
      )

    self.hash2 =\
      TransactionHash(
        b'TESTVALUE9DONTUSEINPRODUCTION99999CIGCCF'
        b'KIUFZF9EP9YEYGQAIEXDTEAAUGAEWBBASHYCWBHDX'
      )

  def test_wireup(self):
    """
    Verify that the command is wired up correctly.
    """
    self.assertIsInstance(
      Iota(self.adapter).getLatestInclusion,
      GetLatestInclusionCommand,
    )

  def test_happy_path(self):
    """
    Successfully requesting latest inclusion state.
    """
    self.adapter.seed_response('getNodeInfo', {
        # ``getNodeInfo`` returns lots of info, but the only value that
        # matters for this test is ``latestSolidSubtangleMilestone``.
        'latestSolidSubtangleMilestone': self.milestone,
      },
    )

    self.adapter.seed_response('getInclusionStates', {
      'states': [True, False],
    })

    response = self.command(hashes=[self.hash1, self.hash2])

    self.assertDictEqual(
      response,

      {
        'states': {
          self.hash1: True,
          self.hash2: False,
        },
      }
    )
コード例 #5
0
class GetInputsCommandTestCase(TestCase):
    # noinspection SpellCheckingInspection
    def setUp(self):
        super(GetInputsCommandTestCase, self).setUp()

        self.adapter = MockAdapter()
        self.command = GetInputsCommand(self.adapter)

        # Define some valid tryte sequences that we can reuse between
        # tests.
        self.addy0 =\
          Address(
            trytes =
              b'TESTVALUE9DONTUSEINPRODUCTION99999FIODSG'
              b'IC9CCIFCNBTBDFIEHHE9RBAEVGK9JECCLCPBIINAX',

            key_index = 0,
          )

        self.addy1 =\
          Address(
            trytes =
              b'TESTVALUE9DONTUSEINPRODUCTION999999EPCNH'
              b'MBTEH9KDVFMHHESDOBTFFACCGBFGACEDCDDCGICIL',

            key_index = 1,
          )

        self.addy2 =\
          Address(
            trytes =
              b'TESTVALUE9DONTUSEINPRODUCTION99999YDOHWF'
              b'U9PFOFHGKFACCCBGDALGI9ZBEBABFAMBPDSEQ9XHJ',

            key_index = 2,
          )

    def test_wireup(self):
        """
    Verify that the command is wired up correctly.
    """
        self.assertIsInstance(
            Iota(self.adapter).getInputs,
            GetInputsCommand,
        )

    def test_stop_threshold_met(self):
        """
    ``stop`` provided, balance meets ``threshold``.
    """
        self.adapter.seed_response('getBalances', {
            'balances': [42, 29],
        })

        # To keep the unit test nice and speedy, we will mock the address
        # generator.  We already have plenty of unit tests for that
        # functionality, so we can get away with mocking it here.
        mock_address_generator = mock.Mock(
            return_value=[self.addy0, self.addy1])

        with mock.patch(
                'iota_async.crypto.addresses.AddressGenerator.get_addresses',
                mock_address_generator,
        ):
            response = self.command(
                seed=Seed.random(),
                stop=2,
                threshold=71,
            )

        self.assertEqual(response['totalBalance'], 71)
        self.assertEqual(len(response['inputs']), 2)

        input0 = response['inputs'][0]
        self.assertIsInstance(input0, Address)

        self.assertEqual(input0, self.addy0)
        self.assertEqual(input0.balance, 42)
        self.assertEqual(input0.key_index, 0)

        input1 = response['inputs'][1]
        self.assertIsInstance(input1, Address)

        self.assertEqual(input1, self.addy1)
        self.assertEqual(input1.balance, 29)
        self.assertEqual(input1.key_index, 1)

    def test_stop_threshold_not_met(self):
        """
    ``stop`` provided, balance does not meet ``threshold``.
    """
        self.adapter.seed_response('getBalances', {
            'balances': [42, 29],
        })

        # To keep the unit test nice and speedy, we will mock the address
        # generator.  We already have plenty of unit tests for that
        # functionality, so we can get away with mocking it here.
        mock_address_generator = mock.Mock(
            return_value=[self.addy0, self.addy1])

        with mock.patch(
                'iota_async.crypto.addresses.AddressGenerator.get_addresses',
                mock_address_generator,
        ):
            with self.assertRaises(BadApiResponse):
                self.command(
                    seed=Seed.random(),
                    stop=2,
                    threshold=72,
                )

    def test_stop_threshold_zero(self):
        """
    ``stop`` provided, ``threshold`` is 0.
    """
        # Note that the first address has a zero balance.
        self.adapter.seed_response('getBalances', {
            'balances': [0, 1],
        })

        # To keep the unit test nice and speedy, we will mock the address
        # generator.  We already have plenty of unit tests for that
        # functionality, so we can get away with mocking it here.
        mock_address_generator = mock.Mock(
            return_value=[self.addy0, self.addy1])

        with mock.patch(
                'iota_async.crypto.addresses.AddressGenerator.get_addresses',
                mock_address_generator,
        ):
            response = self.command(
                seed=Seed.random(),
                stop=2,
                threshold=0,
            )

        self.assertEqual(response['totalBalance'], 1)
        self.assertEqual(len(response['inputs']), 1)

        # Address 0 was skipped because it has a zero balance.
        input0 = response['inputs'][0]
        self.assertIsInstance(input0, Address)

        self.assertEqual(input0, self.addy1)
        self.assertEqual(input0.balance, 1)
        self.assertEqual(input0.key_index, 1)

    def test_stop_no_threshold(self):
        """
    ``stop`` provided, no ``threshold``.
    """
        self.adapter.seed_response('getBalances', {
            'balances': [42, 29],
        })

        # To keep the unit test nice and speedy, we will mock the address
        # generator.  We already have plenty of unit tests for that
        # functionality, so we can get away with mocking it here.
        mock_address_generator = mock.Mock(
            return_value=[self.addy0, self.addy1])

        with mock.patch(
                'iota_async.crypto.addresses.AddressGenerator.get_addresses',
                mock_address_generator,
        ):
            response = self.command(
                seed=Seed.random(),
                start=0,
                stop=2,
            )

        self.assertEqual(response['totalBalance'], 71)
        self.assertEqual(len(response['inputs']), 2)

        input0 = response['inputs'][0]
        self.assertIsInstance(input0, Address)

        self.assertEqual(input0, self.addy0)
        self.assertEqual(input0.balance, 42)
        self.assertEqual(input0.key_index, 0)

        input1 = response['inputs'][1]
        self.assertIsInstance(input1, Address)

        self.assertEqual(input1, self.addy1)
        self.assertEqual(input1.balance, 29)
        self.assertEqual(input1.key_index, 1)

    def test_no_stop_threshold_met(self):
        """
    No ``stop`` provided, balance meets ``threshold``.
    """
        self.adapter.seed_response('getBalances', {
            'balances': [42, 29],
        })

        # ``getInputs`` uses ``findTransactions`` to identify unused
        # addresses.
        # noinspection SpellCheckingInspection
        self.adapter.seed_response(
            'findTransactions', {
                'hashes': [
                    TransactionHash(
                        b'TESTVALUE9DONTUSEINPRODUCTION99999WBL9KD'
                        b'EIZDMEDFPEYDIIA9LEMEUCC9MFPBY9TEVCUGSEGGN'),
                ],
            })

        # noinspection SpellCheckingInspection
        self.adapter.seed_response(
            'findTransactions', {
                'hashes': [
                    TransactionHash(
                        b'TESTVALUE9DONTUSEINPRODUCTION99999YFXGOD'
                        b'GISBJAX9PDJIRDMDV9DCRDCAEG9FN9KECCBDDFZ9H'),
                ],
            })

        self.adapter.seed_response('findTransactions', {
            'hashes': [],
        })

        # To keep the unit test nice and speedy, we will mock the address
        # generator.  We already have plenty of unit tests for that
        # functionality, so we can get away with mocking it here.
        # noinspection PyUnusedLocal
        def mock_address_generator(ag, start, step=1):
            for addy in [self.addy0, self.addy1, self.addy2][start::step]:
                yield addy

        # When ``stop`` is None, the command uses a generator internally.
        with mock.patch(
                'iota_async.crypto.addresses.AddressGenerator.create_iterator',
                mock_address_generator,
        ):
            response = self.command(
                seed=Seed.random(),
                threshold=71,
            )

        self.assertEqual(response['totalBalance'], 71)
        self.assertEqual(len(response['inputs']), 2)

        input0 = response['inputs'][0]
        self.assertIsInstance(input0, Address)

        self.assertEqual(input0, self.addy0)
        self.assertEqual(input0.balance, 42)
        self.assertEqual(input0.key_index, 0)

        input1 = response['inputs'][1]
        self.assertIsInstance(input1, Address)

        self.assertEqual(input1, self.addy1)
        self.assertEqual(input1.balance, 29)
        self.assertEqual(input1.key_index, 1)

    def test_no_stop_threshold_not_met(self):
        """
    No ``stop`` provided, balance does not meet ``threshold``.
    """
        self.adapter.seed_response('getBalances', {
            'balances': [42, 29, 0],
        })

        # To keep the unit test nice and speedy, we will mock the address
        # generator.  We already have plenty of unit tests for that
        # functionality, so we can get away with mocking it here.
        # noinspection PyUnusedLocal
        def mock_address_generator(ag, start, step=1):
            for addy in [self.addy0, self.addy1, self.addy2][start::step]:
                yield addy

        # When ``stop`` is None, the command uses a generator internally.
        with mock.patch(
                'iota_async.crypto.addresses.AddressGenerator.create_iterator',
                mock_address_generator,
        ):
            with self.assertRaises(BadApiResponse):
                self.command(
                    seed=Seed.random(),
                    threshold=72,
                )

    def test_no_stop_threshold_zero(self):
        """
    No ``stop`` provided, ``threshold`` is 0.
    """
        # Note that the first address has a zero balance.
        self.adapter.seed_response('getBalances', {
            'balances': [0, 1],
        })

        # ``getInputs`` uses ``findTransactions`` to identify unused
        # addresses.
        # noinspection SpellCheckingInspection
        self.adapter.seed_response(
            'findTransactions', {
                'hashes': [
                    TransactionHash(
                        b'TESTVALUE9DONTUSEINPRODUCTION99999WBL9KD'
                        b'EIZDMEDFPEYDIIA9LEMEUCC9MFPBY9TEVCUGSEGGN'),
                ],
            })

        # noinspection SpellCheckingInspection
        self.adapter.seed_response(
            'findTransactions', {
                'hashes': [
                    TransactionHash(
                        b'TESTVALUE9DONTUSEINPRODUCTION99999YFXGOD'
                        b'GISBJAX9PDJIRDMDV9DCRDCAEG9FN9KECCBDDFZ9H'),
                ],
            })

        self.adapter.seed_response('findTransactions', {
            'hashes': [],
        })

        # To keep the unit test nice and speedy, we will mock the address
        # generator.  We already have plenty of unit tests for that
        # functionality, so we can get away with mocking it here.
        # noinspection PyUnusedLocal
        def mock_address_generator(ag, start, step=1):
            for addy in [self.addy0, self.addy1, self.addy2][start::step]:
                yield addy

        # When ``stop`` is None, the command uses a generator internally.
        with mock.patch(
                'iota_async.crypto.addresses.AddressGenerator.create_iterator',
                mock_address_generator,
        ):
            response = self.command(
                seed=Seed.random(),
                threshold=0,
            )

        self.assertEqual(response['totalBalance'], 1)
        self.assertEqual(len(response['inputs']), 1)

        # Because the first address had a zero balance, it was skipped.
        input0 = response['inputs'][0]
        self.assertIsInstance(input0, Address)

        self.assertEqual(input0, self.addy1)
        self.assertEqual(input0.balance, 1)
        self.assertEqual(input0.key_index, 1)

    def test_no_stop_no_threshold(self):
        """
    No ``stop`` provided, no ``threshold``.
    """
        self.adapter.seed_response('getBalances', {
            'balances': [42, 29],
        })

        # ``getInputs`` uses ``findTransactions`` to identify unused
        # addresses.
        # noinspection SpellCheckingInspection
        self.adapter.seed_response(
            'findTransactions', {
                'hashes': [
                    TransactionHash(
                        b'TESTVALUE9DONTUSEINPRODUCTION99999WBL9KD'
                        b'EIZDMEDFPEYDIIA9LEMEUCC9MFPBY9TEVCUGSEGGN'),
                ],
            })

        # noinspection SpellCheckingInspection
        self.adapter.seed_response(
            'findTransactions', {
                'hashes': [
                    TransactionHash(
                        b'TESTVALUE9DONTUSEINPRODUCTION99999YFXGOD'
                        b'GISBJAX9PDJIRDMDV9DCRDCAEG9FN9KECCBDDFZ9H'),
                ],
            })

        self.adapter.seed_response('findTransactions', {
            'hashes': [],
        })

        # To keep the unit test nice and speedy, we will mock the address
        # generator.  We already have plenty of unit tests for that
        # functionality, so we can get away with mocking it here.
        # noinspection PyUnusedLocal
        def mock_address_generator(ag, start, step=1):
            for addy in [self.addy0, self.addy1, self.addy2][start::step]:
                yield addy

        # When ``stop`` is None, the command uses a generator internally.
        with mock.patch(
                'iota_async.crypto.addresses.AddressGenerator.create_iterator',
                mock_address_generator,
        ):
            response = self.command(seed=Seed.random(), )

        self.assertEqual(response['totalBalance'], 71)
        self.assertEqual(len(response['inputs']), 2)

        input0 = response['inputs'][0]
        self.assertIsInstance(input0, Address)

        self.assertEqual(input0, self.addy0)
        self.assertEqual(input0.balance, 42)
        self.assertEqual(input0.key_index, 0)

        input1 = response['inputs'][1]
        self.assertIsInstance(input1, Address)

        self.assertEqual(input1, self.addy1)
        self.assertEqual(input1.balance, 29)
        self.assertEqual(input1.key_index, 1)

    def test_start(self):
        """
    Using ``start`` to offset the key range.
    """
        self.adapter.seed_response('getBalances', {
            'balances': [86],
        })

        # ``getInputs`` uses ``findTransactions`` to identify unused
        # addresses.
        # noinspection SpellCheckingInspection
        self.adapter.seed_response(
            'findTransactions', {
                'hashes': [
                    TransactionHash(
                        b'TESTVALUE9DONTUSEINPRODUCTION99999YFXGOD'
                        b'GISBJAX9PDJIRDMDV9DCRDCAEG9FN9KECCBDDFZ9H'),
                ],
            })

        self.adapter.seed_response('findTransactions', {
            'hashes': [],
        })

        # To keep the unit test nice and speedy, we will mock the address
        # generator.  We already have plenty of unit tests for that
        # functionality, so we can get away with mocking it here.
        # noinspection PyUnusedLocal
        def mock_address_generator(ag, start, step=1):
            # If ``start`` has the wrong value, return garbage to make the
            # test asplode.
            for addy in [None, self.addy1, self.addy2][start::step]:
                yield addy

        # When ``stop`` is None, the command uses a generator internally.
        with mock.patch(
                'iota_async.crypto.addresses.AddressGenerator.create_iterator',
                mock_address_generator,
        ):
            response = self.command(
                seed=Seed.random(),
                start=1,
            )

        self.assertEqual(response['totalBalance'], 86)
        self.assertEqual(len(response['inputs']), 1)

        input0 = response['inputs'][0]
        self.assertIsInstance(input0, Address)

        self.assertEqual(input0, self.addy1)
        self.assertEqual(input0.balance, 86)
        self.assertEqual(input0.key_index, 1)

    def test_start_stop(self):
        """
    Using ``start`` and ``stop`` at once.
    Checking if correct number of addresses is returned. Must be stop - start
    """

        # To keep the unit test nice and speedy, we will mock the address
        # generator.  We already have plenty of unit tests for that
        # functionality, so we can get away with mocking it here.
        # noinspection PyUnusedLocal

        def mock_address_generator(ag, start, step=1):
            # returning up to 3 addresses, depending on stop value
            for addy in [self.addy0, self.addy1, self.addy2][start::step]:
                yield addy

        self.adapter.seed_response('getBalances', {
            'balances': [11, 11],
        })

        with mock.patch(
                'iota_async.crypto.addresses.AddressGenerator.create_iterator',
                mock_address_generator,
        ):
            response = self.command(
                seed=Seed.random(),
                start=1,
                stop=3,
            )

        self.assertEqual(len(response['inputs']),
                         2)  # 3 - 1 = 2 addresses expected
        self.assertEqual(response['totalBalance'], 22)

        input0 = response['inputs'][0]
        self.assertIsInstance(input0, Address)
        self.assertEqual(input0, self.addy1)
        self.assertEqual(input0.balance, 11)
        self.assertEqual(input0.key_index, 1)

        input1 = response['inputs'][1]
        self.assertIsInstance(input1, Address)
        self.assertEqual(input1, self.addy2)
        self.assertEqual(input1.balance, 11)
        self.assertEqual(input1.key_index, 2)

    def test_security_level_1_no_stop(self):
        """
    Testing GetInputsCoommand:
      - with security_level = 1 (non default)
      - without `stop` parameter
    """

        # one address with index 0 for selected security levels for the random seed.
        # to check with respective outputs from command
        seed = Seed.random()
        address = AddressGenerator(seed, security_level=1).get_addresses(0)[0]

        self.adapter.seed_response('getBalances', {
            'balances': [86],
        })
        # ``getInputs`` uses ``findTransactions`` to identify unused
        # addresses.
        # noinspection SpellCheckingInspection
        self.adapter.seed_response(
            'findTransactions', {
                'hashes': [
                    TransactionHash(
                        b'TESTVALUE9DONTUSEINPRODUCTION99999YFXGOD'
                        b'GISBJAX9PDJIRDMDV9DCRDCAEG9FN9KECCBDDFZ9H'),
                ],
            })
        self.adapter.seed_response('findTransactions', {
            'hashes': [],
        })

        response = GetInputsCommand(self.adapter)(
            seed=seed,
            securityLevel=1,
        )

        self.assertEqual(response['totalBalance'], 86)
        self.assertEqual(len(response['inputs']), 1)

        input0 = response['inputs'][0]
        self.assertIsInstance(input0, Address)
        self.assertEqual(input0, address)
        self.assertEqual(input0.balance, 86)
        self.assertEqual(input0.key_index, 0)

    def test_security_level_1_with_stop(self):
        """
    Testing GetInputsCoommand:
      - with security_level = 1 (non default)
      - with `stop` parameter
    """

        # one address with index 0 for selected security levels for the random seed.
        # to check with respective outputs from command
        seed = Seed.random()
        address = AddressGenerator(seed, security_level=1).get_addresses(0)[0]

        self.adapter.seed_response('getBalances', {
            'balances': [86],
        })
        # ``getInputs`` uses ``findTransactions`` to identify unused
        # addresses.
        # noinspection SpellCheckingInspection
        self.adapter.seed_response(
            'findTransactions', {
                'hashes': [
                    TransactionHash(
                        b'TESTVALUE9DONTUSEINPRODUCTION99999YFXGOD'
                        b'GISBJAX9PDJIRDMDV9DCRDCAEG9FN9KECCBDDFZ9H'),
                ],
            })
        self.adapter.seed_response('findTransactions', {
            'hashes': [],
        })

        response = GetInputsCommand(self.adapter)(
            seed=seed,
            securityLevel=1,
            stop=1,  # <<<<< here
        )

        self.assertEqual(response['totalBalance'], 86)
        self.assertEqual(len(response['inputs']), 1)

        input0 = response['inputs'][0]
        self.assertIsInstance(input0, Address)
        self.assertEqual(input0, address)
        self.assertEqual(input0.balance, 86)
        self.assertEqual(input0.key_index, 0)
コード例 #6
0
ファイル: api_test.py プロジェクト: stkubr/iota_async.lib.py
class CustomCommandTestCase(TestCase):
    def setUp(self):
        super(CustomCommandTestCase, self).setUp()

        self.name = 'helloWorld'
        self.adapter = MockAdapter()
        self.command = CustomCommand(self.adapter, self.name)

    def test_call(self):
        """
        Sending a custom command.
        """
        expected_response = {'message': 'Hello, IOTA!'}

        self.adapter.seed_response('helloWorld', expected_response)

        response = self.command()

        self.assertEqual(response, expected_response)
        self.assertTrue(self.command.called)

        self.assertListEqual(
            self.adapter.requests,
            [{'command': 'helloWorld'}],
        )

    def test_call_with_parameters(self):
        """
        Sending a custom command with parameters.
        """
        expected_response = {'message': 'Hello, IOTA!'}

        self.adapter.seed_response('helloWorld', expected_response)

        response = self.command(foo='bar', baz='luhrmann')

        self.assertEqual(response, expected_response)
        self.assertTrue(self.command.called)

        self.assertListEqual(
            self.adapter.requests,
            [{'command': 'helloWorld', 'foo': 'bar', 'baz': 'luhrmann'}],
        )

    def test_call_error_already_called(self):
        """
        A command can only be called once.
        """
        self.adapter.seed_response('helloWorld', {})
        self.command()

        with self.assertRaises(RuntimeError):
            self.command(extra='params')

        self.assertDictEqual(self.command.request, {'command': 'helloWorld'})

    def test_call_reset(self):
        """
        Resetting a command allows it to be called more than once.
        """
        self.adapter.seed_response('helloWorld', {'message': 'Hello, IOTA!'})
        self.command()

        self.command.reset()

        self.assertFalse(self.command.called)
        self.assertIsNone(self.command.request)
        self.assertIsNone(self.command.response)

        expected_response = {'message': 'Welcome back!'}
        self.adapter.seed_response('helloWorld', expected_response)
        response = self.command(foo='bar')

        self.assertDictEqual(response, expected_response)
        self.assertDictEqual(self.command.response, expected_response)

        self.assertDictEqual(
            self.command.request,

            {
                'command': 'helloWorld',
                'foo': 'bar',
            },
        )
コード例 #7
0
class PrepareMultisigTransferCommandTestCase(TestCase):
  # noinspection SpellCheckingInspection
  def setUp(self):
    super(PrepareMultisigTransferCommandTestCase, self).setUp()

    self.adapter = MockAdapter()
    self.command = PrepareMultisigTransferCommand(self.adapter)

    # Define some tryte sequences that we can reuse between tests.
    self.digest_1 =\
      Digest(
        trytes =
          b'FWNEPVJNGUKTSHSBDO9AORBCVWWLVXC9KAMKYYNKPYNJDKSAUURI9ELKOEEYPKVTYP'
          b'CKOCJQESYFEMINIFKX9PDDGRBEEHYYXCJW9LHGWFZGHKCPVDBGMGQKIPCNKNITGMZT'
          b'DIWVUB9PCHCOPHMIWKSUKRHZOJPMAY',

        key_index = 0,
      )

    self.digest_2 =\
      Digest(
        trytes =
          b'PAIRLDJQY9XAUSKIGCTHRJHZVARBEY9NNHYJ9UI9HWWZXFSDWEZEGDCWNVVYSYDV9O'
          b'HTR9NGGZURISWTNECFTCMEWQQFJ9VKLFPDTYJYXC99OLGRH9OSFJLMEOGHFDHZYEAF'
          b'IMIZTJRBQUVCR9U9ZWTMUXTUEOUBLC',

        key_index = 0,
      )

    self.trytes_1 = (
      b'TESTVALUE9DONTUSEINPRODUCTION99999IIPEM9'
      b'LA9FLHEGHDACSA9DOBQHQCX9BBHCFDIIMACARHA9B'
    )

    self.trytes_2 = (
      b'TESTVALUE9DONTUSEINPRODUCTION99999BGUDVE'
      b'DGH9WFQDEDVETCOGEGCDI9RFHGFGXBI99EJICHNEM'
    )

    self.trytes_3 = (
      b'TESTVALUE9DONTUSEINPRODUCTION99999XBGEUC'
      b'LF9EIFXHM9KHQANBLBHFVGTEGBWHNAKFDGZHYGCHI'
    )

  def test_wireup(self):
    """
    Verifies the command is wired up correctly.
    """
    self.assertIsInstance(
      MultisigIota(self.adapter).prepareMultisigTransfer,
      PrepareMultisigTransferCommand,
    )

  def test_happy_path(self):
    """
    Preparing a bundle with a multisig input.
    """
    self.adapter.seed_response(
      command = GetBalancesCommand.command,

      response = {
        'balances': [42],

        # Would it be cheeky to put "7½ million years" here?
        'duration': 86,
      },
    )

    pmt_result =\
      self.command(
        transfers = [
          ProposedTransaction(
            address = Address(self.trytes_1),
            value   = 42,
          ),
        ],

        multisigInput =
          MultisigAddress(
            digests = [self.digest_1, self.digest_2],
            trytes  = self.trytes_2,
          ),
      )

    # The command returns the raw trytes.  This is useful in a
    # real-world scenario because trytes are easier to transfer between
    # each entity that needs to apply their signature.
    #
    # However, for purposes of this test, we will convert the trytes
    # back into a bundle so that we can inspect the end result more
    # easily.
    bundle = Bundle.from_tryte_strings(pmt_result['trytes'])

    #
    # This bundle looks almost identical to what you would expect from
    # :py:meth:`iota_async.api.Iota.prepare_transfer`, except:
    # - There are 4 inputs (to hold all of the signature fragments).
    # - The inputs are unsigned.
    #
    self.assertEqual(len(bundle), 5)

    # Spend Transaction
    txn_1 = bundle[0]
    self.assertEqual(txn_1.address, self.trytes_1)
    self.assertEqual(txn_1.value, 42)

    # Input 1, Part 1 of 4
    txn_2 = bundle[1]
    self.assertEqual(txn_2.address, self.trytes_2)
    self.assertEqual(txn_2.value, -42)
    self.assertEqual(txn_2.signature_message_fragment, Fragment(b''))

    # Input 1, Part 2 of 4
    txn_3 = bundle[2]
    self.assertEqual(txn_3.address, self.trytes_2)
    self.assertEqual(txn_3.value, 0)
    self.assertEqual(txn_3.signature_message_fragment, Fragment(b''))

    # Input 1, Part 3 of 4
    txn_4 = bundle[3]
    self.assertEqual(txn_4.address, self.trytes_2)
    self.assertEqual(txn_4.value, 0)
    self.assertEqual(txn_4.signature_message_fragment, Fragment(b''))

    # Input 1, Part 4 of 4
    txn_5 = bundle[4]
    self.assertEqual(txn_5.address, self.trytes_2)
    self.assertEqual(txn_5.value, 0)
    self.assertEqual(txn_5.signature_message_fragment, Fragment(b''))

  def test_unspent_inputs_with_change_address(self):
    """
    The bundle has unspent inputs, so it uses the provided change
    address.
    """
    self.adapter.seed_response(
      command = GetBalancesCommand.command,

      response = {
        'balances': [101],
        'duration': 86,
      },
    )

    pmt_result =\
      self.command(
        transfers = [
          ProposedTransaction(
            address = Address(self.trytes_1),
            value   = 42,
          ),
        ],

        multisigInput =
          MultisigAddress(
            digests = [self.digest_1, self.digest_2],
            trytes  = self.trytes_2,
          ),

        changeAddress = Address(self.trytes_3),
      )

    bundle = Bundle.from_tryte_strings(pmt_result['trytes'])

    self.assertEqual(len(bundle), 6)

    # Spend Transaction
    txn_1 = bundle[0]
    self.assertEqual(txn_1.address, self.trytes_1)
    self.assertEqual(txn_1.value, 42)

    # Input 1, Part 1 of 4
    txn_2 = bundle[1]
    self.assertEqual(txn_2.address, self.trytes_2)
    self.assertEqual(txn_2.value, -101)
    self.assertEqual(txn_2.signature_message_fragment, Fragment(b''))

    # Input 1, Part 2 of 4
    txn_3 = bundle[2]
    self.assertEqual(txn_3.address, self.trytes_2)
    self.assertEqual(txn_3.value, 0)
    self.assertEqual(txn_3.signature_message_fragment, Fragment(b''))

    # Input 1, Part 3 of 4
    txn_4 = bundle[3]
    self.assertEqual(txn_4.address, self.trytes_2)
    self.assertEqual(txn_4.value, 0)
    self.assertEqual(txn_4.signature_message_fragment, Fragment(b''))

    # Input 1, Part 4 of 4
    txn_5 = bundle[4]
    self.assertEqual(txn_5.address, self.trytes_2)
    self.assertEqual(txn_5.value, 0)
    self.assertEqual(txn_5.signature_message_fragment, Fragment(b''))

    # Change
    txn_6 = bundle[5]
    self.assertEqual(txn_6.address, self.trytes_3)
    self.assertEqual(txn_6.value, 59)

  def test_error_zero_iotas_transferred(self):
    """
    The bundle doesn't spend any IOTAs.

    This is considered an error case because
    :py:meth:`MultisigIota.prepare_multisig_transfer` is specialized
    for generating bundles that require multisig inputs.  Any bundle
    that doesn't require multisig functionality should be generated
    using :py:meth:`iota_async.api.Iota.prepare_transfer` instead.
    """
    with self.assertRaises(ValueError):
      self.command(
        transfers = [
          ProposedTransaction(
            address = Address(self.trytes_1),
            value   = 0,
          ),
        ],

        multisigInput =
          MultisigAddress(
            digests = [self.digest_1, self.digest_2],
            trytes  = self.trytes_2,
          ),
      )

  def test_error_insufficient_inputs(self):
    """
    The multisig input does not contain sufficient IOTAs to cover the
    spends.
    """
    self.adapter.seed_response(
      command = GetBalancesCommand.command,

      response = {
        'balances': [42],
        'duration': 86,
      },
    )

    with self.assertRaises(ValueError):
      self.command(
        transfers = [
          ProposedTransaction(
            address = Address(self.trytes_1),
            value   = 101,
          ),
        ],

        multisigInput =
          MultisigAddress(
            digests = [self.digest_1, self.digest_2],
            trytes  = self.trytes_2,
          ),
      )

  def test_error_unspent_inputs_no_change_address(self):
    """
    The bundle has unspent inputs, but no change address was specified.

    Unlike :py:meth:`iota_async.api.Iota.prepare_transfer` where all of the
    inputs are owned by the same seed, creating a multisig transfer
    usually involves multiple people.

    It would be unfair to the participants of the transaction if we
    were to automatically generate a change address using the seed of
    whoever happened to invoke the
    :py:meth:`MultisigIota.prepare_multisig_transfer` method!
    """
    self.adapter.seed_response(
      command = GetBalancesCommand.command,

      response = {
        'balances': [101],
        'duration': 86,
      },
    )

    with self.assertRaises(ValueError):
      self.command(
        transfers = [
          ProposedTransaction(
            address = Address(self.trytes_1),
            value   = 42,
          ),
        ],

        multisigInput =
          MultisigAddress(
            digests = [self.digest_1, self.digest_2],
            trytes  = self.trytes_2,
          ),

        # changeAddress = Address(self.trytes_3),
      )
コード例 #8
0
class SendTrytesCommandTestCase(TestCase):
    # noinspection SpellCheckingInspection
    def setUp(self):
        super(SendTrytesCommandTestCase, self).setUp()

        self.adapter = MockAdapter()
        self.command = SendTrytesCommand(self.adapter)

        # Define a few valid values that we can reuse across tests.
        self.trytes1 = b'RBTC9D9DCDQAEASBYBCCKBFA'
        self.trytes2 =\
          b'CCPCBDVC9DTCEAKDXC9D9DEARCWCPCBDVCTCEAHDWCTCEAKDCDFD9DSCSA'

        self.transaction1 = (b'TKGDZ9GEI9CPNQGHEATIISAKYPPPSXVCXBSR9EIW'
                             b'CTHHSSEQCD9YLDPEXYERCNJVASRGWMAVKFQTC9999')

        self.transaction2 = (b'TKGDZ9GEI9CPNQGHEATIISAKYPPPSXVCXBSR9EIW'
                             b'CTHHSSEQCD9YLDPEXYERCNJVASRGWMAVKFQTC9999')

    def test_wireup(self):
        """
    Verify that the command is wired up correctly.
    """
        self.assertIsInstance(
            Iota(self.adapter).sendTrytes,
            SendTrytesCommand,
        )

    def test_happy_path(self):
        """
    Successful invocation of ``sendTrytes``.
    """
        self.adapter.seed_response(
            'getTransactionsToApprove', {
                'trunkTransaction': text_type(self.transaction1, 'ascii'),
                'branchTransaction': text_type(self.transaction2, 'ascii'),
            })

        self.adapter.seed_response(
            'attachToTangle', {
                'trytes': [
                    text_type(self.trytes1, 'ascii'),
                    text_type(self.trytes2, 'ascii'),
                ],
            })

        self.adapter.seed_response('broadcastTransactions', {})
        self.adapter.seed_response('storeTransactions', {})

        trytes = [
            TransactionTrytes(self.trytes1),
            TransactionTrytes(self.trytes2),
        ]

        response = self.command(
            trytes=trytes,
            depth=100,
            minWeightMagnitude=18,
        )

        self.assertDictEqual(response, {'trytes': trytes})
コード例 #9
0
class GetNewAddressesCommandTestCase(TestCase):
  # noinspection SpellCheckingInspection
  def setUp(self):
    super(GetNewAddressesCommandTestCase, self).setUp()

    self.adapter = MockAdapter()
    self.command = GetNewAddressesCommand(self.adapter)

    self.seed =\
      Seed(
        b'TESTVALUE9DONTUSEINPRODUCTION99999ZDCCUF'
        b'CBBIQCLGMEXAVFQEOF9DRAB9VCEBAGXAF9VF9FLHP',
      )

    self.addy_1 =\
      Address(
        b'NYMWLBUJEISSACZZBRENC9HEHYQXHCGQHSNHVCEA'
        b'ZDCTEVNGSDUEKTSYBSQGMVJRIEDHWDYSEYCFAZAH9',
      )

    self.addy_2 =\
      Address(
        b'NTPSEVZHQITARYWHIRTSIFSERINLRYVXLGIQKKHY'
        b'IWYTLQUUHDWSOVXLIKVJTYZBFKLABWRBFYVSMD9NB',
      )

    self.addy_1_checksum =\
      Address(
        b'NYMWLBUJEISSACZZBRENC9HEHYQXHCGQHSNHVCEA'
        b'ZDCTEVNGSDUEKTSYBSQGMVJRIEDHWDYSEYCFAZAH'
        b'9T9FPJROTW',
      )

  def test_wireup(self):
    """
    Verify that the command is wired up correctly.
    """
    self.assertIsInstance(
      Iota(self.adapter).getNewAddresses,
      GetNewAddressesCommand,
    )

  def test_get_addresses_offline(self):
    """
    Generate addresses in offline mode (without filtering used
    addresses).
    """
    response =\
      self.command(
        count = 2,
        index = 0,
        seed  = self.seed,
      )

    self.assertDictEqual(
      response,
      {'addresses': [self.addy_1, self.addy_2]},
    )

    # No API requests were made.
    self.assertListEqual(self.adapter.requests, [])

  def test_security_level(self):
    """
    Generating addresses with a different security level.
    """
    response =\
      self.command(
        count         = 2,
        index         = 0,
        securityLevel = 1,
        seed          = self.seed,
      )

    # noinspection SpellCheckingInspection
    self.assertDictEqual(
      response,

      {
        'addresses':
          [
            Address(
              b'ERBTZTPT9SKDQEGETKMZLYNRQMZYZIDENGWCSGRF'
              b'9TLURIEFVKUBSWOIMLMWTWMWTTHSUREPISXDPLCQC',
            ),

            Address(
              b'QVHEMGYHVMCFAISJKTWPFSKDAFRZHXQZK9E9KOUQ'
              b'LOLVBN9BFAZDDY9O9EYYMHMDWZAKXI9OPBPEYM9FC',
            ),
          ],
      },
    )

  def test_get_addresses_online(self):
    """
    Generate address in online mode (filtering used addresses).
    """
    # Pretend that ``self.addy1`` has already been used, but not
    # ``self.addy2``.
    # noinspection SpellCheckingInspection
    self.adapter.seed_response('findTransactions', {
      'duration': 18,

      'hashes': [
        'TESTVALUE9DONTUSEINPRODUCTION99999ITQLQN'
        'LPPG9YNAARMKNKYQO9GSCSBIOTGMLJUFLZWSY9999',
      ],
    })

    self.adapter.seed_response('findTransactions', {
      'duration': 1,
      'hashes':   [],
    })

    response =\
      self.command(
        # If ``count`` is missing or ``None``, the command will operate
        # in online mode.
        # count = None,

        index = 0,
        seed  = self.seed,
      )

    # The command determined that ``self.addy1`` was already used, so
    # it skipped that one.
    self.assertDictEqual(response, {'addresses': [self.addy_2]})

    self.assertListEqual(
      self.adapter.requests,

      # The command issued two `findTransactions` API requests: one for
      # each address generated, until it found an unused address.
      [
        {
          'command':    'findTransactions',
          'addresses':  [self.addy_1],
        },

        {
          'command':    'findTransactions',
          'addresses':  [self.addy_2],
        },
      ],
    )

  def test_new_address_checksum(self):
    """
    Generate address with a checksum.
    """
    response =\
      self.command(
        checksum      = True,
        count         = 1,
        index         = 0,
        seed          = self.seed,
      )

    self.assertDictEqual(
      response,
      {'addresses': [self.addy_1_checksum]},
    )
コード例 #10
0
class PromoteTransactionCommandTestCase(TestCase):
    def setUp(self):
        super(PromoteTransactionCommandTestCase, self).setUp()

        self.adapter = MockAdapter()
        self.command = PromoteTransactionCommand(self.adapter)

        self.trytes1 = b'RBTC9D9DCDQAEASBYBCCKBFA'
        self.trytes2 =\
          b'CCPCBDVC9DTCEAKDXC9D9DEARCWCPCBDVCTCEAHDWCTCEAKDCDFD9DSCSA'

        self.hash1 = TransactionHash(
            b'TESTVALUE9DONTUSEINPRODUCTION99999TBPDM9'
            b'ADFAWCKCSFUALFGETFIFG9UHIEFE9AYESEHDUBDDF')

    def test_wireup(self):
        """
    Verifies that the command is wired-up correctly.
    """
        self.assertIsInstance(
            Iota(self.adapter).promoteTransaction,
            PromoteTransactionCommand,
        )

    def test_happy_path(self):
        """
    Successfully promoting a bundle.
    """

        self.adapter.seed_response('checkConsistency', {
            'state': True,
        })

        result_bundle = Bundle.from_tryte_strings([
            TransactionTrytes(self.trytes1),
            TransactionTrytes(self.trytes2),
        ])
        mock_send_transfer = mock.Mock(return_value={
            'bundle': result_bundle,
        })

        with mock.patch(
                'iota_async.commands.extended.send_transfer.SendTransferCommand._execute',
                mock_send_transfer,
        ):

            response = self.command(
                transaction=self.hash1,
                depth=3,
                minWeightMagnitude=16,
            )

        self.assertDictEqual(response, {
            'bundle': result_bundle,
        })

    def test_not_promotable(self):
        """
    Bundle isn't promotable.
    """

        self.adapter.seed_response('checkConsistency', {
            'state': False,
        })

        with self.assertRaises(BadApiResponse):
            response = self.command(
                transaction=self.hash1,
                depth=3,
                minWeightMagnitude=16,
            )
コード例 #11
0
class CheckConsistencyCommandTestCase(TestCase):
  # noinspection SpellCheckingInspection
  def setUp(self):
    super(CheckConsistencyCommandTestCase, self).setUp()

    self.adapter = MockAdapter()
    self.command = CheckConsistencyCommand(self.adapter)

    # Define some tryte sequences that we can re-use across tests.
    self.milestone =\
      TransactionHash(
        b'TESTVALUE9DONTUSEINPRODUCTION99999W9KDIH'
        b'BALAYAFCADIDU9HCXDKIXEYDNFRAKHN9IEIDZFWGJ'
      )

    self.hash1 =\
      TransactionHash(
        b'TESTVALUE9DONTUSEINPRODUCTION99999TBPDM9'
        b'ADFAWCKCSFUALFGETFIFG9UHIEFE9AYESEHDUBDDF'
      )

    self.hash2 =\
      TransactionHash(
        b'TESTVALUE9DONTUSEINPRODUCTION99999CIGCCF'
        b'KIUFZF9EP9YEYGQAIEXDTEAAUGAEWBBASHYCWBHDX'
      )

  def test_wireup(self):
    """
    Verify that the command is wired up correctly.
    """
    self.assertIsInstance(
      Iota(self.adapter).checkConsistency,
      CheckConsistencyCommand,
    )

  def test_happy_path(self):
    """
    Successfully checking consistency.
    """

    self.adapter.seed_response('checkConsistency', {
      'state': True,
    })

    response = self.command(tails=[self.hash1, self.hash2])

    self.assertDictEqual(
      response,

      {
        'state': True,
      }
    )

  def test_info_with_false_state(self):
    """
    `info` field exists when `state` is False.
    """

    self.adapter.seed_response('checkConsistency', {
      'state': False,
      'info': 'Additional information',
    })

    response = self.command(tails=[self.hash1, self.hash2])

    self.assertDictEqual(
      response,

      {
        'state': False,
        'info': 'Additional information',
      }
    )
コード例 #12
0
class GetBundlesCommandTestCase(TestCase):
  def setUp(self):
    super(GetBundlesCommandTestCase, self).setUp()

    self.adapter = MockAdapter()
    self.command = GetBundlesCommand(self.adapter)

  def test_wireup(self):
    """
    Verifies that the command is wired up correctly.
    """
    self.assertIsInstance(
      Iota(self.adapter).getBundles,
      GetBundlesCommand,
    )

  def test_single_transaction(self):
    """
    Getting a bundle that contains a single transaction.
    """
    transaction =\
      Transaction(
          current_index                     = 0,
          last_index                        = 0,
          tag                               = Tag(b''),
          timestamp                         = 1484960990,
          value                             = 0,
          attachment_timestamp              = 1484960990,
          attachment_timestamp_lower_bound  = 12,
          attachment_timestamp_upper_bound  = 0,

          # These values are not relevant for 0-value transactions.
          nonce                       = Nonce(b''),
          signature_message_fragment  = Fragment(b''),

          # This value is computed automatically, so it has to be real.
          hash_ =
            TransactionHash(
              b'XPJIYZWPF9LBCYZPNBFARDRCSUGJGF9TWZT9K9PX'
              b'VYDFPZOZBGXUCKLTJEUCFBEKQQ9VCSQVQDMMJQAY9',
            ),

          address =
            Address(
              b'TESTVALUE9DONTUSEINPRODUCTION99999OCSGVF'
              b'IBQA99KGTCPCZ9NHR9VGLGADDDIEGGPCGBDEDDTBC',
            ),

          bundle_hash =
            BundleHash(
              b'TESTVALUE9DONTUSEINPRODUCTION99999DIOAZD' 
              b'M9AIUHXGVGBC9EMGI9SBVBAIXCBFJ9EELCPDRAD9U',
            ),

          branch_transaction_hash =
            TransactionHash(
              b'TESTVALUE9DONTUSEINPRODUCTION99999BBCEDI'
              b'ZHUDWBYDJEXHHAKDOCKEKDFIMB9AMCLFW9NBDEOFV',
            ),

          trunk_transaction_hash =
            TransactionHash(
              b'TESTVALUE9DONTUSEINPRODUCTION999999ARAYA'
              b'MHCB9DCFEIWEWDLBCDN9LCCBQBKGDDAECFIAAGDAS',
            ),
        )

    self.adapter.seed_response('getTrytes', {
      'trytes': [transaction.as_tryte_string()],
    })

    response = self.command(transaction=transaction.hash)

    bundle = response['bundles'][0] # type: Bundle
    self.assertEqual(len(bundle), 1)

    self.maxDiff = None
    self.assertDictEqual(
      bundle[0].as_json_compatible(),
      transaction.as_json_compatible(),
    )

  def test_multiple_transactions(self):
    """
    Getting a bundle that contains multiple transactions.
    """
    bundle = Bundle.from_tryte_strings([
      TransactionTrytes(
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999WUQXEGBVIECGIWO9IGSYKWWPYCIVUJJGSJPWGIAFJPYSF9NSQOHWAHS9P'
        b'9PWQHOBXNNQIF9IRHVQXKPZW999999999999999999999999999XZUIENOTTBKJMDP'
        b'RXWGQYG9PWGTHNLFMVD99A99999999A99999999PDQWLVVDPUU9VIBODGMRIAZPGQX'
        b'DOGSEXIHKIBWSLDAWUKZCZMK9Z9YZSPCKBDJSVDPRQLJSTKUMTNVSXBGUEHHGAIWWQ'
        b'BCJZHZAQOWZMAIDAFUZBVMUVPWQJLUGGQKNKLMGTWXXNZKUCBJLEDAMYVRGABAWBY9'
        b'999MYIYBTGIOQYYZFJBLIAWMPSZEFFTXUZPCDIXSLLQDQSFYGQSQOGSPKCZNLVSZ9L'
        b'MCUWVNGEN9EJEW9999XZUIENOTTBKJMDPRXWGQYG9PWGTXUO9AXMP9FLMDRMADLRPW'
        b'CZCJBROYCDRJMYU9HDYJM9NDBFUPIZVTR'
      ),

      # Well, it was bound to happen sooner or later... the ASCII
      # representation of this tryte sequence contains a very naughty
      # phrase.  But I don't feel like doing another POW, so... enjoy.
      TransactionTrytes(
        b'NBTCPCFDEACCPCBDVC9DTCQAJ9RBTC9D9DCDQAEAKDCDFD9DSCFAJ9VBCDJDTCQAJ9'
        b'ZBMDYBCCKB99999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999SYRABNN9JD9PNDL'
        b'IKUNCECUELTOHNLFMVD99999999999A99999999PDQWLVVDPUU9VIBODGMRIAZPGQX'
        b'DOGSEXIHKIBWSLDAWUKZCZMK9Z9YZSPCKBDJSVDPRQLJSTKUMTNVSXFSEWUNJOEGNU'
        b'I9QOCRFMYSIFAZLJHKZBPQZZYFG9ORYCRDX9TOMJPFCRB9R9KPUUGFPVOWYXFIWEW9'
        b'999BGUEHHGAIWWQBCJZHZAQOWZMAIDAFUZBVMUVPWQJLUGGQKNKLMGTWXXNZKUCBJL'
        b'EDAMYVRGABAWBY9999SYRABNN9JD9PNDLIKUNCECUELTOQZPSBDILVHJQVCEOICFAD'
        b'YKZVGMOAXJRQNTCKMHGTAUMPGJJMX9LNF'
      ),
    ])

    for txn in bundle:
      self.adapter.seed_response('getTrytes', {
        'trytes': [txn.as_tryte_string()],
      })

    self.adapter.seed_response('getTrytes', {
      'trytes': [
        'SPAMSPAMSPAM999999999999999999999999999999999999999999999999999999'
        '999999999999999999999999999999999999999999999999999999999999999999'
        '999999999999999999999999999999999999999999999999999999999999999999'
        '999999999999999999999999999999999999999999999999999999999999999999'
        '999999999999999999999999999999999999999999999999999999999999999999'
        '999999999999999999999999999999999999999999999999999999999999999999'
        '999999999999999999999999999999999999999999999999999999999999999999'
        '999999999999999999999999999999999999999999999999999999999999999999'
        '999999999999999999999999999999999999999999999999999999999999999999'
        '999999999999999999999999999999999999999999999999999999999999999999'
        '999999999999999999999999999999999999999999999999999999999999999999'
        '999999999999999999999999999999999999999999999999999999999999999999'
        '999999999999999999999999999999999999999999999999999999999999999999'
        '999999999999999999999999999999999999999999999999999999999999999999'
        '999999999999999999999999999999999999999999999999999999999999999999'
        '999999999999999999999999999999999999999999999999999999999999999999'
        '999999999999999999999999999999999999999999999999999999999999999999'
        '999999999999999999999999999999999999999999999999999999999999999999'
        '999999999999999999999999999999999999999999999999999999999999999999'
        '999999999999999999999999999999999999999999999999999999999999999999'
        '999999999999999999999999999999999999999999999999999999999999999999'
        '999999999999999999999999999999999999999999999999999999999999999999'
        '999999999999999999999999999999999999999999999999999999999999999999'
        '999999999999999999999999999999999999999999999999999999999999999999'
        '999999999999999999999999999999999999999999999999999999999999999999'
        '999999999999999999999999999999999999999999999999999999999999999999'
        '999999999999999999999999999999999999999999999999999999999999999999'
        '999999999999999999999999999999999999999999999999999999999999999999'
        '999999999999999999999999999999999999999999999999999999999999999999'
        '999999999999999999999999999999999999999999999999999999999999999999'
        '999999999999999999999999999999999999999999999999999999999999999999'
        '999999999999999999999999999999999999999999999999999999999999999999'
        '999999999999999999999999999999999999999999999999999999999999999999'
        '999999999999999999999999999999999999999999999999999999999999999999'
        '999999999999999999999999999999999999999999999999999JECDITWO9999999'
        '999999999999ONLFMVD99999999999999999999VVCHSQSRVFKSBONDWB9EAQEMQOY'
        'YRBIZHTBJLYNAVDHZPUZAZ9LYHXWKBEJ9IPR9FAMFLT9EEOHVYWUPRHHSRCILCLWFD'
        'GBYBFFOKMCSAPVD9VGZZRRGBLGMZMXD9RMZQDBLMGN9BATWZGULRBCYQEIKIRBPHC9'
        '999KTLTRSYOWBD9HVNP9GCUABARNGMYXUZKXWRPGOPETZLKYYC9Z9EYXIWVARUBMBM'
        'BPXGORN9WPBLY99999ZRBVQWULRFXDNDYZKRKIXPZQT9JJJH9FZU9PVWZJWLXBPODP'
        'EHMKTTAGEPLPHUQCZNLDSHERONOMHJCOI'
      ],
    })

    response = self.command(
      transaction =
        TransactionHash(
          b'TOYJPHKMLQNDVLDHDILARUJCCIUMQBLUSWPCTIVA'
          b'DRXICGYDGSVPXFTILFFGAPICYHGGJ9OHXINFX9999'
        ),
    )
    self.maxDiff = None
    self.assertListEqual(
      response['bundles'][0].as_json_compatible(),
      bundle.as_json_compatible(),
    )

  def test_non_tail_transaction(self):
    """
    Trying to get a bundle for a non-tail transaction.

    This is not valid; you have to start with a tail transaction.
    """
    self.adapter.seed_response('getTrytes', {
      'trytes': [
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999999999999999999999999999999999999999999999999999999999999'
        b'999999999WUQXEGBVIECGIWO9IGSYKWWPYCIVUJJGSJPWGIAFJPYSF9NSQOHWAHS9P'
        b'9PWQHOBXNNQIF9IRHVQXKPZW999999999999999999999999999999999999999999'
        b'999999999999HNLFMVD99A99999999A99999999PDQWLVVDPUU9VIBODGMRIAZPGQX'
        b'DOGSEXIHKIBWSLDAWUKZCZMK9Z9YZSPCKBDJSVDPRQLJSTKUMTNVSXBGUEHHGAIWWQ'
        b'BCJZHZAQOWZMAIDAFUZBVMUVPWQJLUGGQKNKLMGTWXXNZKUCBJLEDAMYVRGABAWBY9'
        b'999MYIYBTGIOQYYZFJBLIAWMPSZEFFTXUZPCDIXSLLQDQSFYGQSQOGSPKCZNLVSZ9L'
        b'MCUWVNGEN9EJEW9999XZUIENOTTBKJMDPRXWGQYG9PWGTXUO9AXMP9FLMDRMADLRPW'
        b'CZCJBROYCDRJMYU9HDYJM9NDBFUPIZVTR'
      ],
    })

    with self.assertRaises(BadApiResponse):
      self.command(
        transaction =
          TransactionHash(
            b'FSEWUNJOEGNUI9QOCRFMYSIFAZLJHKZBPQZZYFG9'
            b'ORYCRDX9TOMJPFCRB9R9KPUUGFPVOWYXFIWEW9999'
          ),
      )

  def test_missing_transaction(self):
    """
    Unable to find the requested transaction.
    """
    self.adapter.seed_response('getTrytes', {'trytes': []})

    with self.assertRaises(BadApiResponse):
      self.command(
        transaction =
          TransactionHash(
            b'FSEWUNJOEGNUI9QOCRFMYSIFAZLJHKZBPQZZYFG9'
            b'ORYCRDX9TOMJPFCRB9R9KPUUGFPVOWYXFIWEW9999'
          ),
      )
コード例 #13
0
class GetTransfersCommandTestCase(TestCase):
    def setUp(self):
        super(GetTransfersCommandTestCase, self).setUp()

        self.adapter = MockAdapter()
        self.command = GetTransfersCommand(self.adapter)

        # Define some tryte sequences we can re-use between tests.
        self.addy1 =\
          Address(
            b'TESTVALUEONE9DONTUSEINPRODUCTION99999YDZ'
            b'E9TAFAJGJA9CECKDAEPHBICDR9LHFCOFRBQDHC9IG'
          )

        self.addy2 =\
          Address(
            b'TESTVALUETWO9DONTUSEINPRODUCTION99999TES'
            b'GINEIDLEEHRAOGEBMDLENFDAFCHEIHZ9EBZDD9YHL'
          )

    def test_wireup(self):
        """
    Verify that the command is wired up correctly.
    """
        self.assertIsInstance(
            Iota(self.adapter).getTransfers,
            GetTransfersCommand,
        )

    def test_full_scan(self):
        """
    Scanning the Tangle for all transfers.
    """

        # To speed up the test, we will mock the address generator.
        # :py:class:`iota_async.crypto.addresses.AddressGenerator` already has
        # its own test case, so this does not impact the stability of the
        # codebase.
        # noinspection PyUnusedLocal
        def create_generator(ag, start, step=1):
            for addy in [self.addy1, self.addy2][start::step]:
                yield addy

        # The first address received IOTA.
        self.adapter.seed_response(
            'findTransactions',
            {
                'duration':
                42,
                'hashes': [
                    'TESTVALUEFIVE9DONTUSEINPRODUCTION99999VH'
                    'YHRHJETGYCAFZGABTEUBWCWAS9WF99UHBHRHLIOFJ',
                ],
            },
        )

        # The second address is unused.
        self.adapter.seed_response(
            'findTransactions',
            {
                'duration': 1,
                'hashes': [],
            },
        )

        self.adapter.seed_response(
            'getTrytes',
            {
                'duration': 99,

                # Thankfully, we do not have to seed a realistic response for
                # ``getTrytes``, as we will be mocking the ``getBundles``
                # command that uses on it.
                'trytes': [''],
            },
        )

        bundle = Bundle([
            Transaction(
                address=self.addy1,
                timestamp=1483033814,

                # These values are not relevant to the test.
                hash_=None,
                signature_message_fragment=None,
                value=42,
                tag=Tag(b''),
                current_index=0,
                last_index=0,
                bundle_hash=None,
                trunk_transaction_hash=None,
                branch_transaction_hash=None,
                attachment_timestamp=1483033814,
                attachment_timestamp_lower_bound=12,
                attachment_timestamp_upper_bound=0,
                nonce=None,
            )
        ])

        mock_get_bundles =\
          mock.Mock(return_value={
            'bundles': [bundle],
          })

        with mock.patch(
                'iota_async.crypto.addresses.AddressGenerator.create_iterator',
                create_generator,
        ):
            with mock.patch(
                    'iota_async.commands.extended.get_bundles.GetBundlesCommand._execute',
                    mock_get_bundles,
            ):
                response = self.command(seed=Seed.random())

        self.assertDictEqual(
            response,
            {
                'bundles': [bundle],
            },
        )

    def test_no_transactions(self):
        """
    There are no transactions for the specified seed.
    """

        # To speed up the test, we will mock the address generator.
        # :py:class:`iota_async.crypto.addresses.AddressGenerator` already has
        # its own test case, so this does not impact the stability of the
        # codebase.
        # noinspection PyUnusedLocal
        def create_generator(ag, start, step=1):
            for addy in [self.addy1][start::step]:
                yield addy

        self.adapter.seed_response(
            'findTransactions',
            {
                'duration': 1,
                'hashes': [],
            },
        )

        with mock.patch(
                'iota_async.crypto.addresses.AddressGenerator.create_iterator',
                create_generator,
        ):
            response = self.command(seed=Seed.random())

        self.assertDictEqual(response, {'bundles': []})

    def test_start(self):
        """
    Scanning the Tangle for all transfers, with start index.
    """

        # noinspection PyUnusedLocal
        def create_generator(ag, start, step=1):
            # Inject an invalid value into the generator, to ensure it is
            # skipped.
            for addy in [None, self.addy1, self.addy2][start::step]:
                yield addy

        # The first address received IOTA.
        self.adapter.seed_response(
            'findTransactions',
            {
                'duration':
                42,
                'hashes': [
                    'TESTVALUEFIVE9DONTUSEINPRODUCTION99999VH'
                    'YHRHJETGYCAFZGABTEUBWCWAS9WF99UHBHRHLIOFJ',
                ],
            },
        )

        # The second address is unused.
        self.adapter.seed_response(
            'findTransactions',
            {
                'duration': 1,
                'hashes': [],
            },
        )

        self.adapter.seed_response(
            'getTrytes',
            {
                'duration': 99,
                'trytes': [''],
            },
        )

        bundle = Bundle([
            Transaction(
                address=self.addy1,
                timestamp=1483033814,

                # These values are not relevant to the test.
                hash_=None,
                signature_message_fragment=None,
                value=42,
                tag=Tag(b''),
                current_index=0,
                last_index=0,
                bundle_hash=None,
                trunk_transaction_hash=None,
                branch_transaction_hash=None,
                attachment_timestamp=1483033814,
                attachment_timestamp_lower_bound=12,
                attachment_timestamp_upper_bound=0,
                nonce=None,
            )
        ])

        mock_get_bundles = mock.Mock(return_value={
            'bundles': [bundle],
        })

        with mock.patch(
                'iota_async.crypto.addresses.AddressGenerator.create_iterator',
                create_generator,
        ):
            with mock.patch(
                    'iota_async.commands.extended.get_bundles.GetBundlesCommand._execute',
                    mock_get_bundles,
            ):
                response = self.command(seed=Seed.random(), start=1)

        self.assertDictEqual(
            response,
            {
                'bundles': [bundle],
            },
        )

    def test_stop(self):
        """
    Scanning the Tangle for all transfers, with stop index.
    """

        # noinspection PyUnusedLocal
        def create_generator(ag, start, step=1):
            # Inject an invalid value into the generator, to ensure it is
            # skipped.
            for addy in [self.addy1, None][start::step]:
                yield addy

        # The first address received IOTA.
        self.adapter.seed_response(
            'findTransactions',
            {
                'duration':
                42,
                'hashes': [
                    'TESTVALUEFIVE9DONTUSEINPRODUCTION99999VH'
                    'YHRHJETGYCAFZGABTEUBWCWAS9WF99UHBHRHLIOFJ',
                ],
            },
        )

        self.adapter.seed_response(
            'getTrytes',
            {
                'duration': 99,
                'trytes': [''],
            },
        )

        bundle = Bundle([
            Transaction(
                address=self.addy1,
                timestamp=1483033814,

                # These values are not relevant to the test.
                hash_=None,
                signature_message_fragment=None,
                value=42,
                tag=Tag(b''),
                current_index=0,
                last_index=0,
                bundle_hash=None,
                trunk_transaction_hash=None,
                branch_transaction_hash=None,
                attachment_timestamp=1483033814,
                attachment_timestamp_lower_bound=12,
                attachment_timestamp_upper_bound=0,
                nonce=None,
            )
        ])

        mock_get_bundles = mock.Mock(return_value={
            'bundles': [bundle],
        })

        with mock.patch(
                'iota_async.crypto.addresses.AddressGenerator.create_iterator',
                create_generator,
        ):
            with mock.patch(
                    'iota_async.commands.extended.get_bundles.GetBundlesCommand._execute',
                    mock_get_bundles,
            ):
                response = self.command(seed=Seed.random(), stop=1)

        self.assertDictEqual(
            response,
            {
                'bundles': [bundle],
            },
        )

    def test_get_inclusion_states(self):
        """
    Fetching inclusion states with transactions.
    """

        # noinspection PyUnusedLocal
        def create_generator(ag, start, step=1):
            for addy in [self.addy1][start::step]:
                yield addy

        # The first address received IOTA.
        self.adapter.seed_response(
            'findTransactions',
            {
                'duration':
                42,
                'hashes': [
                    'TESTVALUEFIVE9DONTUSEINPRODUCTION99999VH'
                    'YHRHJETGYCAFZGABTEUBWCWAS9WF99UHBHRHLIOFJ',
                ],
            },
        )

        # For this test, we have to generate a real TryteString.
        transaction_trytes =\
          TryteString(
            b'KMYUMNEUAYODAQSNGWTAERRRHNZBZCOLMVVOBTVWLOFYCJKYMGRAMH9RQ9MTZOSZMH'
            b'QNZFHFEJEDFQ99HSUNVOTULDJGXEDULS9ZHABVDZODJUMCNWVCPNSCUVKVYWCEXBHW'
            b'RBZBSWFPQLWZWMUPGQIGAEGOVE9DDXBVCIPKQYCFZFBELTSMVFSIXLPTACTKAFMCTK'
            b'CPYD9BWDJMLKWAOBDSJNQYAHS9GFIQKZCROLFZJVUEIVXVNBRRLEIWTYVHURUXHSCG'
            b'DKEIEGPOCXKCYWIBUG9ABYCALYJVFLBNGMS9ARHGTQXBZFLENXCJVKHPVKD9KSAEOL'
            b'FFVAJCNKLDVHOCDARWUNKARDYMVKFKRSMUTYOUXSBFFYTKRREBDJZTLVUROQFCBXQN'
            b'SXDDYTZTEBRSXOBMLXHJKSJAVOOVCXATOWNQDWHT9CCUAAJUJKDOQLMAEZACSNFKXZ'
            b'IGWDQEUEFRZYAOSDNVMSXWYLVDAUXZSHNHAIBEMNPFUGORYUETNJK9UCEMSUJYBBDK'
            b'BHIPKEINQCGOVYCPKUPJMUCUVZOJSIWYRFMFXYUVSMOUALAQBWIMXBUBXSAETGKJRP'
            b'AHVAXHQJDMEVSRFYEXUSIEBKMGYCUKFD9JPGUV9AIYUVCRUURKMYUHMVE9OJCYYWTQ'
            b'WUWFMTBZYFXASHHVCMSWXKBRQFHHQVEQMEULJRWZKLWFFSGGKEHUZZFNDNITSRAUH9'
            b'PQK9OGLYMVBSHXQLLZHOBBIM9KVUWDLHZRDKQQVLQXGWYXEEVQPDZUO9PVXMALOMRQ'
            b'VCTHGIZLILSCFKTBRESYZGBZKHXEODNDJZ9GK9ROWYXNGFHZCCBHHZEYEOGWXRGSUD'
            b'SUZFUAUBXVXZHCUVJSYBWTCYCEDYKZNGWFZYKSQLW9FUYMWDVXKZEWT9SCVMQCODZK'
            b'DRNKTINTPNOJOLGQJDAJMFWRFSWZJLYZGSTSIDSXLUJBZRZNLEDNBKAUNGTCYUPDRW'
            b'JOCEBQ9YG9IZLLRMJITISJOTLQMOGXVQIZXHMTJVMMWM9FOIOT9KFZMANEPOEOV9HX'
            b'JNEGURUKRWDGYNPVGAWMWQVABIJNL9MDXKONEPMYACOZ9BE9UZMAFTKYWPFWIQWAPK'
            b'GUXQTOQVWYYVZYGQDLBIQDVOZIWGOMGOBAUARICQZVNXD9UVEFBBAJKQBHRHXTBUOW'
            b'VBFKYQWZWTMMXVKZRIZUBVPQ9XHLJHFHWFZUIZVSNAKBDHDFGJCYQETOMEDTOXIUT9'
            b'OAJVIHWAGTCNPEZTERMMN9EZEWSJHKQAUMXPBZTNQOEQCVXIMAAYO9NIUFLTCFIMK9'
            b'9AFAGWJFA9VOFPUDJLRAMORGSUDBLWWKXEDZ9XPQUZSGANGESHKKGGQSGSYDCRLHZD'
            b'PKA9HKYBKLKKCXYRQQIPXCFETJJDZYPCLUNHGBKEJDRCIHEXKCQQNOV9QFHLGFXOCR'
            b'HPAFCUTPMY9NOZVQHROYJSCMGRSVMOBWADAZNFIAHWGIQUUZBOVODSFAUNRTXSDU9W'
            b'EIRBXQNRSJXFRAQGHA9DYOQJGLVZUJKAQ9CTUOTT9ZKQOQNNLJDUPDXZJYPRCVLRZT'
            b'UCZPNBREYCCKHK9FUWGITAJATFPUOFLZDHPNJYUTXFGNYJOBRD9BVHKZENFXIUYDTL'
            b'CE9JYIIYMXMCXMWTHOLTQFKFHDLVPGMQNITEUXSYLAQULCZOJVBIPYP9M9X9QCNKBX'
            b'W9DVJEQFFY9KQVMKNVTAHQVRXUKEM9FZOJLHAGEECZBUHOQFZOSPRXKZOCCKAOHMSV'
            b'QCFG9CWAHKVWNA9QTLYQI9NKOSHWJCNGPJBLEQPUIWJBIOAWKLBXUCERTSL9FVCLYN'
            b'ADPYTPKJOIEMAQGWBVGSRCZINXEJODUDCT9FHOUMQM9ZHRMBJYSOMPNMEAJGEHICJI'
            b'PVXRKCYX9RZVT9TDZIMXGZJAIYJRGIVMSOICSUINRBQILMJOUQYXCYNJ9WGGJFHYTU'
            b'LWOIPUXXFNTIFNOJRZFSQQNAWBQZOLHHLVGHEPWTKKQEVIPVWZUN9ZBICZ9DZZBVII'
            b'BF9EPHARZJUFJGBQXQFQIBUECAWRSEKYJNYKNSVBCOWTFBZ9NAHFSAMRBPEYGPRGKW'
            b'WTWACZOAPEOECUO9OTMGABJVAIICIPXGSXACVINSYEQFTRCQPCEJXZCY9XZWVWVJRZ'
            b'CYEYNFUUBKPWCHICGJZXKE9GSUDXZYUAPLHAKAHYHDXNPHENTERYMMBQOPSQIDENXK'
            b'LKCEYCPVTZQLEEJVYJZV9BWU999999999999999999999999999FFL999999999999'
            b'9999999999999RJQGVD99999999999A99999999USGBXHGJUEWAUAKNPPRHJXDDMQV'
            b'YDSYZJSDWFYLOQVFGBOSLE9KHFDLDYHUYTXVSFAFCOCLQUHJXTEIQRNBTLHEGJFGVF'
            b'DJCE9IKAOCSYHLCLWPVVNWNESKLYAJG9FGGZOFXCEYOTWLVIJUHGY9QCU9FMZJY999'
            b'9999HYBUYQKKRNAVDPVGYBTVDZ9SVQBLCCVLJTPEQWWOIG9CQZIFQKCROH9YHUCNJT'
            b'SYPBVZVBNESX999999D9TARGPQTNIYRZURQGVHCAWEDRBJIIEJIUZYENVE9LLJQMXH'
            b'GSUUYUCPSOWBCXVFDCHHAZUDC9LUODYWO'
          )

        self.adapter.seed_response(
            'getTrytes',
            {
                'duration': 99,
                'trytes': [binary_type(transaction_trytes)],
            },
        )

        transaction = Transaction.from_tryte_string(transaction_trytes)

        mock_get_bundles = mock.Mock(return_value={
            'bundles': [Bundle([transaction])],
        })

        mock_get_latest_inclusion = mock.Mock(return_value={
            'states': {
                transaction.hash: True,
            },
        })

        with mock.patch(
                'iota_async.crypto.addresses.AddressGenerator.create_iterator',
                create_generator,
        ):
            with mock.patch(
                    'iota_async.commands.extended.get_bundles.GetBundlesCommand._execute',
                    mock_get_bundles,
            ):
                with mock.patch(
                        'iota_async.commands.extended.get_latest_inclusion.GetLatestInclusionCommand._execute',
                        mock_get_latest_inclusion,
                ):
                    response = self.command(
                        seed=Seed.random(),
                        inclusionStates=True,

                        # To keep the test focused, only retrieve a single
                        # transaction.
                        start=0,
                        stop=1,
                    )

        bundle = response['bundles'][0]  # type: Bundle
        self.assertTrue(bundle[0].is_confirmed)