Exemple #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(
            cornode(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})
Exemple #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'},
        )
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(
            cornode(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,

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

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

              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'9PWQHOBXNNQIF9IRHVQXKPZW999999999999999999999999999999999999999999'
                b'999999999999HNLFMVD99A99999999A99999999PDQWLVVDPUU9VIBODGMRIAZPGQX'
                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'999999999999999999999999999999999999999999999999999999999999999999'
                b'999999999999HNLFMVD99999999999A99999999PDQWLVVDPUU9VIBODGMRIAZPGQX'
                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'), )
class GetNewAddressesCommandTestCase(TestCase):
  def setUp(self):
    super(GetNewAddressesCommandTestCase, self).setUp()

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

    # Create a few TryteStrings we can reuse across tests.
    self.addy1 =\
      Address(
        b'ADDYONE999AHHKVD9SBEYWQFNVQSNTGYQSQ9AGWD'
        b'JDZKBYCVTODUHFEVVMNMPQMIXOVXVCZRUENAWYNTO'
      )

    self.addy2 =\
      Address(
        b'ADDYTWO999AGAQKYXHRMSFAQNPWCIYUYTXPWUEUR'
        b'VNZTCTFUPQ9ESTKNSSLLIZWDQISJVEWIJDVGIECXF'
      )

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

  def test_get_addresses_offline(self):
    """
    Generate addresses in offline mode (without filtering used
    addresses).
    """
    # To speed up the test, we will mock the address generator.
    # :py:class:`cornode.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

    with patch(
        target  = 'cornode.crypto.addresses.AddressGenerator.create_iterator',
        new     = create_generator,
    ):
      response = self.command(
        count = 2,
        index = 0,
        seed  = b'TESTSEED9DONTUSEINPRODUCTION99999',
      )

    self.assertDictEqual(response, {'addresses': [self.addy1, self.addy2]})

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

  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``.
    self.adapter.seed_response('findTransactions', {
      'duration': 18,

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

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

    # To speed up the test, we will mock the address generator.
    # :py:class:`cornode.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

    with patch(
        target  = 'cornode.crypto.addresses.AddressGenerator.create_iterator',
        new     = create_generator,
    ):
      response = self.command(
        # If ``count`` is missing or ``None``, the command will operate
        # in online mode.
        # count = None,
        index = 0,
        seed  = b'TESTSEED9DONTUSEINPRODUCTION99999',
      )

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

    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.addy1],
          'approvees':  [],
          'bundles':    [],
          'tags':       [],
        },

        {
          'command':    'findTransactions',
          'addresses':  [self.addy2],
          'approvees':  [],
          'bundles':    [],
          'tags':       [],
        },
      ],
    )
Exemple #5
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(
            cornode(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,
            },
        })
Exemple #6
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(
            cornode(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})
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(
            cornode(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(return_value=[self.addy0, self.addy1])

        with patch(
                'cornode.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(return_value=[self.addy0, self.addy1])

        with patch(
                'cornode.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(return_value=[self.addy0, self.addy1])

        with patch(
                'cornode.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(return_value=[self.addy0, self.addy1])

        with patch(
                'cornode.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 patch(
                'cornode.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 patch(
                'cornode.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 patch(
                'cornode.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 patch(
                'cornode.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 patch(
                'cornode.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)
Exemple #8
0
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, cornode!'}

        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, cornode!'}

        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, cornode!'})
        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',
            },
        )
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(
      cornode(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:`cornode.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 cornode.
    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,
        nonce = None,
      )
    ])

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

    with patch(
        'cornode.crypto.addresses.AddressGenerator.create_iterator',
        create_generator,
    ):
      with patch(
          'cornode.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:`cornode.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 patch(
        'cornode.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 cornode.
    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,
        nonce = None,
      )
    ])

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

    with patch(
        'cornode.crypto.addresses.AddressGenerator.create_iterator',
        create_generator,
    ):
      with patch(
          'cornode.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 cornode.
    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,
        nonce = None,
      )
    ])

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

    with patch(
        'cornode.crypto.addresses.AddressGenerator.create_iterator',
        create_generator,
    ):
      with patch(
          'cornode.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 cornode.
    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(return_value={
      'bundles': [Bundle([transaction])],
    })

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

    with patch(
        'cornode.crypto.addresses.AddressGenerator.create_iterator',
        create_generator,
    ):
      with patch(
          'cornode.commands.extended.get_bundles.GetBundlesCommand._execute',
          mock_get_bundles,
      ):
        with patch(
          'cornode.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)