diff --git a/docs/commands.rst b/docs/commands.rst new file mode 100644 index 00000000..a860a441 --- /dev/null +++ b/docs/commands.rst @@ -0,0 +1,217 @@ +Advanced: PyOTA Commands +======================== + +.. note:: + **This page contains information about how PyOTA works under the hood.** + + It is absolutely not necessary to be familiar with the content described + below if you just want to use the library. + + However, if you are a curious mind or happen to do development on the + library, the following information might be useful. + +PyOTA provides the API interface (:ref:`Core API Methods` and +:ref:`Extended API Methods`) for users of the library. These handle +constructing and sending HTTP requests to the specified node through adapters, +furthermore creating, transforming and translating between PyOTA-specific types +and (JSON-encoded) raw data. They also filter outgoing requests and incoming +responses to ensure that only appropriate data is communicated with the node. + +PyOTA implements the `Command Design Pattern`_. High level API interface +methods (:ref:`Core API Methods` and :ref:`Extended API Methods`) +internally call PyOTA commands to get the job done. + +Most PyOTA commands are sub-classed from :py:class:`FilterCommand` class, which +is in turn sub-classed from :py:class:`BaseCommand` class. The reason for the +2-level inheritance is simple: separating functionality. As the name implies, +:py:class:`FilterCommand` adds filtering capabilities to +:py:class:`BaseCommand`, that contains the logic of constructing the request +and using its adapter to send it and receive a response. + +Command Flow +------------ +As mentioned earlier, API methods rely on PyOTA commands to carry out +specific operations. It is important to understand what happens during command +execution so you are able to implement new methods that extend the current +capabilities of PyOTA. + +.. py:currentmodule:: iota + +Let's investigate the process through an example of a core API method, for +instance :py:meth:`~Iota.find_transactions`, that calls +:py:class:`FindTransactionCommand` PyOTA command internally. + +.. note:: + :py:class:`FindTransactionCommand` is sub-classed from :py:class:`FilterCommand`. + +To illustrate what the happens inside the API method, take a look at the +following figure + + +.. figure:: images/command_execution.svg + :scale: 100 % + :alt: Inner workings of a PyOTA Command. + + Inner workings of a PyOTA Command. + +- When you call :py:meth:`~Iota.find_transactions` core API method, it + initializes a :py:class:`FindTransactionCommand` object with the adapter of the + API instance it belongs to. + +- Then calls this command with the keyword arguments it was provided with. + +- The command prepares the request by applying a ``RequestFilter`` on the + payload. The command specific ``RequestFilter`` validates that the payload + has correct types, in some cases it is even able to convert the payload to + the required type and format. + +- Command execution injects the name of the API command (see `IRI API Reference`_ + for command names) in the request and sends it to the adapter. + +- The adapter communicates with the node and returns its response. + +- The response is prepared by going through a command-specific + ``ResponseFilter``. + +- The response is returned to the high level API method as a ``dict``, ready + to be returned to the main application. + +.. note:: + A command object can only be called once without resetting it. When you + use the high level API methods, you don't need to worry about resetting + commands as each call to an API method will initialize a new command object. + +Filters +------- + +If you take a look at the actual implementation of +:py:class:`FindTransactionsCommand`, you notice that you have to define your +own request and response filter classes. + +Filters in PyOTA are based on the `Filters library`_. Read more about how +they work at the `filters documentation site`_. + +In short, you can create filter chains through which the filtered value passes, +and generates errors if something failed validation. Filter chains are specified +in the custom filter class's :py:meth:`__init__` function. If you also want to +modify the filtered value before returning it, override the :py:meth:`_apply` +method of its base class. Read more about how to `create custom filters`_. + +PyOTA offers you some custom filters for PyOTA-specific types: + +**Trytes** +~~~~~~~~~~ +.. autoclass:: iota.filters.Trytes + +**StringifiedTrytesArray** +~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. automethod:: iota.filters.StringifiedTrytesArray + +**AddressNoChecksum** +~~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: iota.filters.AddressNoChecksum + +**GeneratedAddress** +~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: iota.filters.GeneratedAddress + +**NodeUri** +~~~~~~~~~~~ +.. autoclass:: iota.filters.NodeUri + :members: SCHEMES + +**SecurityLevel** +~~~~~~~~~~~~~~~~~~~~ +.. automethod:: iota.filters.SecurityLevel + +.. important:: + The general rule in PyOTA is that all requests going to a node are + validated, but only responses that contain transaction/bundle trytes or + hashes are checked. + + Also note, that for extended commands, ``ResponseFilter`` is usually + implemented with just a "pass" statement. The reason being that these + commands do not directly receive their result a node, but rather from + core commands that do have their ``ResponseFilter`` implemented. + More about this topic in the next section. + + +Extended Commands +----------------- + +Core commands, like :py:meth:`~Iota.find_transactions` in the example above, +are for direct communication with the node for simple tasks such +as finding a transaction on the Tangle or getting info about the node. +Extended commands (that serve :ref:`Extended API Methods`) on the other hand +carry out more complex operations such as combining core commands, building +objects, etc... + +As a consequence, extended commands override the default execution phase of their +base class. + +Observe for example :py:class:`FindTransactionObjectsCommand` extended command +that is called in :py:meth:`~Iota.find_transaction_objects` extended API +method. It overrides the :py:meth:`_execute` method of its base class. + +Let's take a closer look at the implementation:: + + ... + def _execute(self, request): + bundles = request\ + .get('bundles') # type: Optional[Iterable[BundleHash]] + addresses = request\ + .get('addresses') # type: Optional[Iterable[Address]] + tags = request\ + .get('tags') # type: Optional[Iterable[Tag]] + approvees = request\ + .get('approvees') # type: Optional[Iterable[TransactionHash]] + + ft_response = FindTransactionsCommand(adapter=self.adapter)( + bundles=bundles, + addresses=addresses, + tags=tags, + approvees=approvees, + ) + + hashes = ft_response['hashes'] + transactions = [] + if hashes: + gt_response = GetTrytesCommand(adapter=self.adapter)(hashes=hashes) + + transactions = list(map( + Transaction.from_tryte_string, + gt_response.get('trytes') or [], + )) # type: List[Transaction] + + return { + 'transactions': transactions, + } + ... + +Instead of sending the request to the adapter, +:py:meth:`FindTransactionObjectsCommand._execute` calls +:py:class:`FindTransactionsCommand` core command, gathers the transaction hashes +that it found, and collects the trytes of those transactions by calling +:py:class:`GetTrytesCommand` core command. Finally, using the obtained trytes, +it constructs a list of transaction objects that are returned to +:py:meth:`~Iota.find_transaction_objects`. + +.. important:: + If you come up with a new functionality for the PyOTA API, please raise + an issue in the `PyOTA Bug Tracker`_ to facilitate discussion. + + Once the community agrees on your proposal, you may start implementing + a new extended API method and the corresponding extended PyOTA command. + + Contributions are always welcome! :) + + Visit the `Contributing to PyOTA`_ page to find out how you can make a + difference! + +.. _Command Design Pattern: https://en.wikipedia.org/wiki/Command_pattern +.. _IRI API Reference: https://docs.iota.org/docs/node-software/0.1/iri/references/api-reference +.. _Filters library: https://pypi.org/project/phx-filters/ +.. _filters documentation site: https://filters.readthedocs.io/en/latest/ +.. _create custom filters: https://filters.readthedocs.io/en/latest/writing_filters.html +.. _PyOTA Bug Tracker: https://github.com/iotaledger/iota.py/issues +.. _Contributing to PyOTA: https://github.com/iotaledger/iota.py/blob/master/CONTRIBUTING.rst \ No newline at end of file diff --git a/docs/images/command_execution.svg b/docs/images/command_execution.svg new file mode 100644 index 00000000..5f1b4b8d --- /dev/null +++ b/docs/images/command_execution.svg @@ -0,0 +1,3 @@ + + +
Create Command Object
- specify adapter
[Not supported by viewer]
Call Command()
- pass keyword arguments
[Not supported by viewer]
Prepare Request
[Not supported by viewer]
Prepare Response
[Not supported by viewer]
Execute Command
[Not supported by viewer]
Return Response To Caller
- dictionary type
[Not supported by viewer]
Reset Command
- if you need the same object
[Not supported by viewer]
Request Filter
[Not supported by viewer]
Response Filter
[Not supported by viewer]
Adapter
[Not supported by viewer]
Node
[Not supported by viewer]
network
[Not supported by viewer]
library
[Not supported by viewer]
\ No newline at end of file diff --git a/docs/index.rst b/docs/index.rst index a8432a97..5c654c0f 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -11,5 +11,6 @@ extended_api addresses multisig + commands .. include:: ../README.rst diff --git a/iota/api.py b/iota/api.py index 7c43c9f5..d0d40b30 100644 --- a/iota/api.py +++ b/iota/api.py @@ -4,13 +4,10 @@ from typing import Dict, Iterable, Optional, Text -from six import add_metaclass - from iota import AdapterSpec, Address, BundleHash, ProposedTransaction, Tag, \ TransactionHash, TransactionTrytes, TryteString, TrytesCompatible from iota.adapter import BaseAdapter, resolve_adapter -from iota.commands import BaseCommand, CustomCommand, core, \ - discover_commands, extended +from iota.commands import BaseCommand, CustomCommand, core, extended from iota.commands.extended.helpers import Helpers from iota.crypto.addresses import AddressGenerator from iota.crypto.types import Seed @@ -28,32 +25,6 @@ class InvalidCommand(ValueError): """ pass - -class ApiMeta(type): - """ - Manages command registries for IOTA API base classes. - """ - - def __init__(cls, name, bases=None, attrs=None): - super(ApiMeta, cls).__init__(name, bases, attrs) - - if not hasattr(cls, 'commands'): - cls.commands = {} - - # Copy command registry from base class to derived class, but - # in the event of a conflict, preserve the derived class' - # commands. - commands = {} - for base in bases: - if isinstance(base, ApiMeta): - commands.update(base.commands) - - if commands: - commands.update(cls.commands) - cls.commands = commands - - -@add_metaclass(ApiMeta) class StrictIota(object): """ API to send HTTP requests for communicating with an IOTA node. @@ -82,7 +53,6 @@ class StrictIota(object): :ref:`find out` how to use it. """ - commands = discover_commands('iota.commands.core') def __init__(self, adapter, testnet=False, local_pow=False): # type: (AdapterSpec, bool, bool) -> None @@ -123,48 +93,6 @@ def __init__(self, adapter, testnet=False, local_pow=False): self.adapter.set_local_pow(local_pow) self.testnet = testnet - def __getattr__(self, command): - # type: (Text) -> BaseCommand - """ - Creates a pre-configured command instance. - - This method will only return commands supported by the API - class. - - If you want to execute an arbitrary API command, use - :py:meth:`create_command`. - - :param Text command: - The name of the command to create. - - References: - - - https://docs.iota.org/docs/node-software/0.1/iri/references/api-reference - """ - # Fix an error when invoking :py:func:`help`. - # https://github.com/iotaledger/iota.py/issues/41 - if command == '__name__': - # noinspection PyTypeChecker - return None - - # Fix an error when invoking dunder methods. - # https://github.com/iotaledger/iota.py/issues/206 - if command.startswith("__"): - # noinspection PyUnresolvedReferences - return super(StrictIota, self).__getattr__(command) - - try: - command_class = self.commands[command] - except KeyError: - raise InvalidCommand( - '{cls} does not support {command!r} command.'.format( - cls=type(self).__name__, - command=command, - ), - ) - - return command_class(self.adapter) - def create_command(self, command): # type: (Text) -> CustomCommand """ @@ -862,7 +790,6 @@ class Iota(StrictIota): - https://docs.iota.org/docs/node-software/0.1/iri/references/api-reference - https://github.com/iotaledger/wiki/blob/master/api-proposal.md """ - commands = discover_commands('iota.commands.extended') def __init__(self, adapter, seed=None, testnet=False, local_pow=False): # type: (AdapterSpec, Optional[TrytesCompatible], bool, bool) -> None diff --git a/iota/commands/__init__.py b/iota/commands/__init__.py index acfefa36..39695bc8 100644 --- a/iota/commands/__init__.py +++ b/iota/commands/__init__.py @@ -18,76 +18,13 @@ __all__ = [ 'BaseCommand', - 'command_registry', - 'discover_commands', 'CustomCommand', 'FilterCommand', 'RequestFilter', 'ResponseFilter', ] -command_registry = {} # type: Dict[Text, CommandMeta] -""" -Registry of commands, indexed by command name. -""" - - -def discover_commands(package, recursively=True): - # type: (Union[ModuleType, Text], bool) -> Dict[Text, 'CommandMeta'] - """ - Automatically discover commands in the specified package. - - :param package: - Package path or reference. - - :param recursively: - If True, will descend recursively into sub-packages. - - :return: - All commands discovered in the specified package, indexed by - command name (note: not class name). - """ - # http://stackoverflow.com/a/25562415/ - if isinstance(package, six.string_types): - package = import_module(package) # type: ModuleType - - commands = {} - - for _, name, is_package in walk_packages(package.__path__, package.__name__ + '.'): - # Loading the module is good enough; the CommandMeta metaclass will - # ensure that any commands in the module get registered. - - # Prefix in name module move to function "walk_packages" for fix - # conflict with names importing packages - # Bug https://github.com/iotaledger/iota.py/issues/63 - sub_package = import_module(name) - - # Index any command classes that we find. - for (_, obj) in get_members(sub_package): - if is_class(obj) and isinstance(obj, CommandMeta): - command_name = getattr(obj, 'command') - if command_name: - commands[command_name] = obj - - if recursively and is_package: - commands.update(discover_commands(sub_package)) - - return commands - -class CommandMeta(ABCMeta): - """ - Automatically register new commands. - """ - # noinspection PyShadowingBuiltins - def __init__(cls, what, bases=None, dict=None): - super(CommandMeta, cls).__init__(what, bases, dict) - - if not is_abstract(cls): - command = getattr(cls, 'command') - if command: - command_registry[command] = cls - -@six.add_metaclass(CommandMeta) +@six.add_metaclass(ABCMeta) class BaseCommand(object): """ An API command ready to send to the node. @@ -265,6 +202,7 @@ class FilterCommand(BaseCommand): """ Uses filters to manipulate request/response values. """ + @abstract_method def get_request_filter(self): # type: () -> Optional[RequestFilter] @@ -336,8 +274,4 @@ def _apply_filter(value, filter_, failure_message): }, ) - return value - - -# Autodiscover commands in this package. -discover_commands(__name__) + return value \ No newline at end of file diff --git a/iota/filters.py b/iota/filters.py index 88f3cbff..9025a817 100644 --- a/iota/filters.py +++ b/iota/filters.py @@ -24,7 +24,13 @@ class GeneratedAddress(f.BaseFilter): """ Validates an incoming value as a generated :py:class:`Address` (must - have ``key_index`` set). + have ``key_index`` and ``security_level`` set). + + When a value doesn't pass the filter, a ``ValueError`` is raised with lots + of contextual info attached to it. + + :return: + :py:class:`GeneratedAddress` object. """ CODE_NO_KEY_INDEX = 'no_key_index' CODE_NO_SECURITY_LEVEL = 'no_security_level' @@ -55,6 +61,12 @@ def _apply(self, value): class NodeUri(f.BaseFilter): """ Validates a string as a node URI. + + When a value doesn't pass the filter, a ``ValueError`` is raised with lots + of contextual info attached to it. + + :return: + :py:class:`NodeUri` object. """ SCHEMES = {'tcp', 'udp'} """ @@ -87,6 +99,9 @@ def _apply(self, value): def SecurityLevel(): """ Generates a filter chain for validating a security level. + + :return: + :py:class:`filters.FilterChain` object. """ return ( f.Type(int) | @@ -99,6 +114,21 @@ def SecurityLevel(): class Trytes(f.BaseFilter): """ Validates a sequence as a sequence of trytes. + + When a value doesn't pass the filter, a ``ValueError`` is raised with lots + of contextual info attached to it. + + :param TryteString result_type: + Any subclass of :py:class:`~iota.TryteString` that you want the filter + to validate. + + :raises TypeError: if value is not of ``result_type``. + :raises ValueError: + if ``result_type`` is not of :py:class:`~iota.TryteString` type. + + :return: + :py:class:`Trytes` object. + """ CODE_NOT_TRYTES = 'not_trytes' CODE_WRONG_FORMAT = 'wrong_format' @@ -190,6 +220,16 @@ def StringifiedTrytesArray(trytes_type=TryteString): strings corresponding to the specified type (e.g., ``TransactionHash``). + When a value doesn't pass the filter, a ``ValueError`` is raised with lots + of contextual info attached to it. + + :param TryteString result_type: + Any subclass of :py:class:`~iota.TryteString` that you want the filter + to validate. + + :return: + :py:class:`filters.FilterChain` object. + .. important:: This filter will return string values, suitable for inclusion in an API request. If you are expecting objects (e.g., @@ -209,8 +249,14 @@ def StringifiedTrytesArray(trytes_type=TryteString): class AddressNoChecksum(Trytes): """ - Validates a sequence as an Address, then chops off the checksum if - present. + Validates a sequence as an :py:class:`Address`, then chops off the checksum + if present. + + When a value doesn't pass the filter, a ``ValueError`` is raised with lots + of contextual info attached to it. + + :return: + :py:class:`AddressNoChecksum` object. """ ADDRESS_BAD_CHECKSUM = 'address_bad_checksum' diff --git a/iota/multisig/api.py b/iota/multisig/api.py index 563f7ca1..0ad14b8d 100644 --- a/iota/multisig/api.py +++ b/iota/multisig/api.py @@ -5,7 +5,6 @@ from typing import Iterable, Optional from iota import Address, Iota, ProposedTransaction -from iota.commands import discover_commands from iota.crypto.addresses import AddressGenerator from iota.crypto.types import Digest from iota.multisig import commands @@ -31,7 +30,6 @@ class MultisigIota(Iota): - https://github.com/iotaledger/wiki/blob/master/multisigs.md """ - commands = discover_commands('iota.multisig.commands') def create_multisig_address(self, digests): # type: (Iterable[Digest]) -> dict diff --git a/test/__init__.py b/test/__init__.py index 83fdaa2f..0b9065db 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -8,7 +8,9 @@ # In Python 3 the ``mock`` library was moved into the stdlib. # noinspection PyUnresolvedReferences from unittest import mock + from unittest.mock import MagicMock, patch else: # In Python 2, the ``mock`` library is included as a dependency. # noinspection PyUnresolvedReferences import mock + from mock import MagicMock, patch diff --git a/test/api_test.py b/test/api_test.py index 7a60e550..95084f4b 100644 --- a/test/api_test.py +++ b/test/api_test.py @@ -107,27 +107,6 @@ def test_init_with_uri(self): api = StrictIota('mock://') self.assertIsInstance(api.adapter, MockAdapter) - def test_registered_command(self): - """ - Preparing a documented command. - """ - api = StrictIota(MockAdapter()) - - # We just need to make sure the correct command type is - # instantiated; individual commands have their own unit tests. - command = api.getNodeInfo - self.assertIsInstance(command, GetNodeInfoCommand) - - def test_unregistered_command(self): - """ - Attempting to create an unsupported command. - """ - api = StrictIota(MockAdapter()) - - with self.assertRaises(InvalidCommand): - # noinspection PyStatementEffect - api.helloWorld - def test_create_command(self): """ Preparing an experimental/undocumented command. diff --git a/test/commands/core/add_neighbors_test.py b/test/commands/core/add_neighbors_test.py index 367f8780..985c3acb 100644 --- a/test/commands/core/add_neighbors_test.py +++ b/test/commands/core/add_neighbors_test.py @@ -6,10 +6,11 @@ import filters as f from filters.test import BaseFilterTestCase -from iota import Iota +from iota import StrictIota from iota.adapter import MockAdapter from iota.commands.core.add_neighbors import AddNeighborsCommand from iota.filters import NodeUri +from test import patch, MagicMock class AddNeighborsRequestFilterTestCase(BaseFilterTestCase): @@ -151,8 +152,20 @@ def setUp(self): def test_wireup(self): """ Verify that the command is wired up correctly. + + The API method indeed calls the appropiate command. """ - self.assertIsInstance( - Iota(self.adapter).addNeighbors, - AddNeighborsCommand, - ) + with patch('iota.commands.core.add_neighbors.AddNeighborsCommand.__call__', + MagicMock(return_value='You found me!') + ) as mocked_command: + + api = StrictIota(self.adapter) + + response = api.add_neighbors('test_uri') + + self.assertTrue(mocked_command.called) + + self.assertEqual( + response, + 'You found me!' + ) \ No newline at end of file diff --git a/test/commands/core/attach_to_tangle_test.py b/test/commands/core/attach_to_tangle_test.py index 787a0ed0..604057e9 100644 --- a/test/commands/core/attach_to_tangle_test.py +++ b/test/commands/core/attach_to_tangle_test.py @@ -11,6 +11,7 @@ from iota.commands.core.attach_to_tangle import AttachToTangleCommand from iota.filters import Trytes from six import binary_type, text_type +from test import patch, MagicMock class AttachToTangleRequestFilterTestCase(BaseFilterTestCase): @@ -423,7 +424,6 @@ def test_pass_happy_path(self): }, ) - class AttachToTangleCommandTestCase(TestCase): def setUp(self): super(AttachToTangleCommandTestCase, self).setUp() @@ -433,8 +433,21 @@ def setUp(self): def test_wireup(self): """ Verify that the command is wired up correctly. + + The API method indeed calls the appropiate command. """ - self.assertIsInstance( - Iota(self.adapter).attachToTangle, - AttachToTangleCommand, - ) + with patch('iota.commands.core.attach_to_tangle.AttachToTangleCommand.__call__', + MagicMock(return_value='You found me!') + ) as mocked_command: + + api = Iota(self.adapter) + + # Don't need to call with proper args here. + response = api.attach_to_tangle('trunk', 'branch', 'trytes') + + self.assertTrue(mocked_command.called) + + self.assertEqual( + response, + 'You found me!' + ) diff --git a/test/commands/core/broadcast_transactions_test.py b/test/commands/core/broadcast_transactions_test.py index 17c104d9..e5c3b73e 100644 --- a/test/commands/core/broadcast_transactions_test.py +++ b/test/commands/core/broadcast_transactions_test.py @@ -13,7 +13,7 @@ from iota.commands.core.broadcast_transactions import \ BroadcastTransactionsCommand from iota.filters import Trytes - +from test import patch, MagicMock class BroadcastTransactionsRequestFilterTestCase(BaseFilterTestCase): filter_type = BroadcastTransactionsCommand(MockAdapter()).get_request_filter @@ -187,8 +187,21 @@ def setUp(self): def test_wireup(self): """ Verify that the command is wired up correctly. + + The API method indeed calls the appropiate command. """ - self.assertIsInstance( - Iota(self.adapter).broadcastTransactions, - BroadcastTransactionsCommand, - ) + with patch('iota.commands.core.broadcast_transactions.BroadcastTransactionsCommand.__call__', + MagicMock(return_value='You found me!') + ) as mocked_command: + + api = Iota(self.adapter) + + # Don't need to call with proper args here. + response = api.broadcast_transactions('trytes') + + self.assertTrue(mocked_command.called) + + self.assertEqual( + response, + 'You found me!' + ) diff --git a/test/commands/core/check_consistency_test.py b/test/commands/core/check_consistency_test.py index 028f39cd..57517df0 100644 --- a/test/commands/core/check_consistency_test.py +++ b/test/commands/core/check_consistency_test.py @@ -11,6 +11,7 @@ from iota.adapter import MockAdapter from iota.commands.core.check_consistency import CheckConsistencyCommand from iota.filters import Trytes +from test import patch, MagicMock class CheckConsistencyRequestFilterTestCase(BaseFilterTestCase): @@ -200,11 +201,24 @@ def setUp(self): def test_wireup(self): """ Verify that the command is wired up correctly. + + The API method indeed calls the appropiate command. """ - self.assertIsInstance( - Iota(self.adapter).checkConsistency, - CheckConsistencyCommand, - ) + with patch('iota.commands.core.check_consistency.CheckConsistencyCommand.__call__', + MagicMock(return_value='You found me!') + ) as mocked_command: + + api = Iota(self.adapter) + + # Don't need to call with proper args here. + response = api.check_consistency('tails') + + self.assertTrue(mocked_command.called) + + self.assertEqual( + response, + 'You found me!' + ) def test_happy_path(self): """ diff --git a/test/commands/core/find_transactions_test.py b/test/commands/core/find_transactions_test.py index cad20fff..3d5a72cb 100644 --- a/test/commands/core/find_transactions_test.py +++ b/test/commands/core/find_transactions_test.py @@ -13,6 +13,7 @@ from iota.commands.core.find_transactions import FindTransactionsCommand, \ FindTransactionsRequestFilter from iota.filters import Trytes +from test import patch, MagicMock class FindTransactionsRequestFilterTestCase(BaseFilterTestCase): @@ -561,8 +562,21 @@ def setUp(self): def test_wireup(self): """ Verify that the command is wired up correctly. + + The API method indeed calls the appropiate command. """ - self.assertIsInstance( - Iota(self.adapter).findTransactions, - FindTransactionsCommand, - ) + with patch('iota.commands.core.check_consistency.CheckConsistencyCommand.__call__', + MagicMock(return_value='You found me!') + ) as mocked_command: + + api = Iota(self.adapter) + + # Don't need to call with proper args here. + response = api.check_consistency('tails') + + self.assertTrue(mocked_command.called) + + self.assertEqual( + response, + 'You found me!' + ) \ No newline at end of file diff --git a/test/commands/core/get_balances_test.py b/test/commands/core/get_balances_test.py index 4f9ecc8b..bfd1450d 100644 --- a/test/commands/core/get_balances_test.py +++ b/test/commands/core/get_balances_test.py @@ -11,6 +11,7 @@ from iota.adapter import MockAdapter from iota.commands.core.get_balances import GetBalancesCommand from iota.filters import Trytes +from test import patch, MagicMock class GetBalancesRequestFilterTestCase(BaseFilterTestCase): @@ -351,8 +352,21 @@ def setUp(self): def test_wireup(self): """ Verify that the command is wired up correctly. + + The API method indeed calls the appropiate command. """ - self.assertIsInstance( - Iota(self.adapter).getBalances, - GetBalancesCommand, - ) + with patch('iota.commands.core.get_balances.GetBalancesCommand.__call__', + MagicMock(return_value='You found me!') + ) as mocked_command: + + api = Iota(self.adapter) + + # Don't need to call with proper args here. + response = api.get_balances('addresses') + + self.assertTrue(mocked_command.called) + + self.assertEqual( + response, + 'You found me!' + ) diff --git a/test/commands/core/get_inclusion_states_test.py b/test/commands/core/get_inclusion_states_test.py index 3a63c754..873a49a1 100644 --- a/test/commands/core/get_inclusion_states_test.py +++ b/test/commands/core/get_inclusion_states_test.py @@ -11,6 +11,7 @@ from iota.commands.core.get_inclusion_states import GetInclusionStatesCommand from iota.filters import Trytes from six import binary_type, text_type +from test import patch, MagicMock class GetInclusionStatesRequestFilterTestCase(BaseFilterTestCase): @@ -259,8 +260,21 @@ def setUp(self): def test_wireup(self): """ Verify that the command is wired up correctly. + + The API method indeed calls the appropiate command. """ - self.assertIsInstance( - Iota(self.adapter).getInclusionStates, - GetInclusionStatesCommand, - ) + with patch('iota.commands.core.get_inclusion_states.GetInclusionStatesCommand.__call__', + MagicMock(return_value='You found me!') + ) as mocked_command: + + api = Iota(self.adapter) + + # Don't need to call with proper args here. + response = api.get_inclusion_states('transactions', 'tips') + + self.assertTrue(mocked_command.called) + + self.assertEqual( + response, + 'You found me!' + ) diff --git a/test/commands/core/get_missing_transactions_test.py b/test/commands/core/get_missing_transactions_test.py index cb6de746..0c5adb00 100644 --- a/test/commands/core/get_missing_transactions_test.py +++ b/test/commands/core/get_missing_transactions_test.py @@ -10,6 +10,7 @@ from iota import Iota, TransactionHash from iota.adapter import MockAdapter from iota.commands.core import GetMissingTransactionsCommand +from test import patch, MagicMock class GetMissingTransactionsRequestFilterTestCase(BaseFilterTestCase): @@ -109,8 +110,21 @@ def setUp(self): def test_wireup(self): """ Verify that the command is wired up correctly. + + The API method indeed calls the appropiate command. """ - self.assertIsInstance( - Iota(self.adapter).getMissingTransactions, - GetMissingTransactionsCommand, - ) + with patch('iota.commands.core.get_missing_transactions.GetMissingTransactionsCommand.__call__', + MagicMock(return_value='You found me!') + ) as mocked_command: + + api = Iota(self.adapter) + + # Don't need to call with proper args here. + response = api.get_missing_transactions() + + self.assertTrue(mocked_command.called) + + self.assertEqual( + response, + 'You found me!' + ) diff --git a/test/commands/core/get_neighbors_test.py b/test/commands/core/get_neighbors_test.py index be7888f5..83e71172 100644 --- a/test/commands/core/get_neighbors_test.py +++ b/test/commands/core/get_neighbors_test.py @@ -9,6 +9,7 @@ from iota import Iota from iota.adapter import MockAdapter from iota.commands.core.get_neighbors import GetNeighborsCommand +from test import patch, MagicMock class GetNeighborsRequestFilterTestCase(BaseFilterTestCase): @@ -49,8 +50,21 @@ def setUp(self): def test_wireup(self): """ Verify that the command is wired up correctly. + + The API method indeed calls the appropiate command. """ - self.assertIsInstance( - Iota(self.adapter).getNeighbors, - GetNeighborsCommand, - ) + with patch('iota.commands.core.get_neighbors.GetNeighborsCommand.__call__', + MagicMock(return_value='You found me!') + ) as mocked_command: + + api = Iota(self.adapter) + + # Don't need to call with proper args here. + response = api.get_neighbors() + + self.assertTrue(mocked_command.called) + + self.assertEqual( + response, + 'You found me!' + ) diff --git a/test/commands/core/get_node_api_configuration_test.py b/test/commands/core/get_node_api_configuration_test.py index bee9e7a7..9f154873 100644 --- a/test/commands/core/get_node_api_configuration_test.py +++ b/test/commands/core/get_node_api_configuration_test.py @@ -10,6 +10,7 @@ from iota import Iota from iota.adapter import MockAdapter from iota.commands.core import GetNodeAPIConfigurationCommand +from test import patch, MagicMock class GetNodeAPIConfigurationRequestFilterTestCase(BaseFilterTestCase): @@ -53,8 +54,21 @@ def setUp(self): def test_wireup(self): """ Verify that the command is wired up correctly. + + The API method indeed calls the appropiate command. """ - self.assertIsInstance( - Iota(self.adapter).getNodeAPIConfiguration, - GetNodeAPIConfigurationCommand, - ) + with patch('iota.commands.core.get_node_api_configuration.GetNodeAPIConfigurationCommand.__call__', + MagicMock(return_value='You found me!') + ) as mocked_command: + + api = Iota(self.adapter) + + # Don't need to call with proper args here. + response = api.get_node_api_configuration() + + self.assertTrue(mocked_command.called) + + self.assertEqual( + response, + 'You found me!' + ) diff --git a/test/commands/core/get_node_info_test.py b/test/commands/core/get_node_info_test.py index 00a34021..9927ed74 100644 --- a/test/commands/core/get_node_info_test.py +++ b/test/commands/core/get_node_info_test.py @@ -9,6 +9,7 @@ from iota import Iota, TransactionHash from iota.adapter import MockAdapter from iota.commands.core.get_node_info import GetNodeInfoCommand +from test import patch, MagicMock class GetNodeInfoRequestFilterTestCase(BaseFilterTestCase): @@ -122,8 +123,21 @@ def setUp(self): def test_wireup(self): """ Verify that the command is wired up correctly. + + The API method indeed calls the appropiate command. """ - self.assertIsInstance( - Iota(self.adapter).getNodeInfo, - GetNodeInfoCommand, - ) + with patch('iota.commands.core.get_node_info.GetNodeInfoCommand.__call__', + MagicMock(return_value='You found me!') + ) as mocked_command: + + api = Iota(self.adapter) + + # Don't need to call with proper args here. + response = api.get_node_info() + + self.assertTrue(mocked_command.called) + + self.assertEqual( + response, + 'You found me!' + ) diff --git a/test/commands/core/get_tips_test.py b/test/commands/core/get_tips_test.py index 03cc9a89..5dfb191d 100644 --- a/test/commands/core/get_tips_test.py +++ b/test/commands/core/get_tips_test.py @@ -11,6 +11,7 @@ from iota.adapter import MockAdapter from iota.commands.core.get_tips import GetTipsCommand from iota.transaction.types import TransactionHash +from test import patch, MagicMock class GetTipsRequestFilterTestCase(BaseFilterTestCase): @@ -120,11 +121,24 @@ def setUp(self): def test_wireup(self): """ Verify that the command is wired up correctly. + + The API method indeed calls the appropiate command. """ - self.assertIsInstance( - Iota(self.adapter).getTips, - GetTipsCommand, - ) + with patch('iota.commands.core.get_tips.GetTipsCommand.__call__', + MagicMock(return_value='You found me!') + ) as mocked_command: + + api = Iota(self.adapter) + + # Don't need to call with proper args here. + response = api.get_tips() + + self.assertTrue(mocked_command.called) + + self.assertEqual( + response, + 'You found me!' + ) def test_type_coercion(self): """ diff --git a/test/commands/core/get_transactions_to_approve_test.py b/test/commands/core/get_transactions_to_approve_test.py index 89559bf0..b5158000 100644 --- a/test/commands/core/get_transactions_to_approve_test.py +++ b/test/commands/core/get_transactions_to_approve_test.py @@ -11,6 +11,7 @@ from iota.commands.core.get_transactions_to_approve import \ GetTransactionsToApproveCommand from iota.filters import Trytes +from test import patch, MagicMock class GetTransactionsToApproveRequestFilterTestCase(BaseFilterTestCase): @@ -218,17 +219,30 @@ def test_pass_happy_path(self): ) -class GetTransactionsToApproveTestCase(TestCase): +class GetTransactionsToApproveCommandTestCase(TestCase): def setUp(self): - super(GetTransactionsToApproveTestCase, self).setUp() + super(GetTransactionsToApproveCommandTestCase, self).setUp() self.adapter = MockAdapter() def test_wireup(self): """ Verify that the command is wired up correctly. + + The API method indeed calls the appropiate command. """ - self.assertIsInstance( - Iota(self.adapter).getTransactionsToApprove, - GetTransactionsToApproveCommand, - ) + with patch('iota.commands.core.get_transactions_to_approve.GetTransactionsToApproveCommand.__call__', + MagicMock(return_value='You found me!') + ) as mocked_command: + + api = Iota(self.adapter) + + # Don't need to call with proper args here. + response = api.get_transactions_to_approve('depth') + + self.assertTrue(mocked_command.called) + + self.assertEqual( + response, + 'You found me!' + ) diff --git a/test/commands/core/get_trytes_test.py b/test/commands/core/get_trytes_test.py index 7d6c2844..2cf36bb0 100644 --- a/test/commands/core/get_trytes_test.py +++ b/test/commands/core/get_trytes_test.py @@ -11,6 +11,7 @@ from iota.adapter import MockAdapter from iota.commands.core.get_trytes import GetTrytesCommand from iota.filters import Trytes +from test import patch, MagicMock class GetTrytesRequestFilterTestCase(BaseFilterTestCase): @@ -242,8 +243,21 @@ def setUp(self): def test_wireup(self): """ Verify that the command is wired up correctly. + + The API method indeed calls the appropiate command. """ - self.assertIsInstance( - Iota(self.adapter).getTrytes, - GetTrytesCommand, - ) + with patch('iota.commands.core.get_trytes.GetTrytesCommand.__call__', + MagicMock(return_value='You found me!') + ) as mocked_command: + + api = Iota(self.adapter) + + # Don't need to call with proper args here. + response = api.get_trytes('hashes') + + self.assertTrue(mocked_command.called) + + self.assertEqual( + response, + 'You found me!' + ) diff --git a/test/commands/core/interrupt_attaching_to_tangle_test.py b/test/commands/core/interrupt_attaching_to_tangle_test.py index 4580b867..3bcb8c76 100644 --- a/test/commands/core/interrupt_attaching_to_tangle_test.py +++ b/test/commands/core/interrupt_attaching_to_tangle_test.py @@ -10,6 +10,7 @@ from iota.adapter import MockAdapter from iota.commands.core.interrupt_attaching_to_tangle import \ InterruptAttachingToTangleCommand +from test import patch, MagicMock class InterruptAttachingToTangleRequestFilterTestCase(BaseFilterTestCase): @@ -48,8 +49,21 @@ def setUp(self): def test_wireup(self): """ Verify that the command is wired up correctly. + + The API method indeed calls the appropiate command. """ - self.assertIsInstance( - Iota(self.adapter).interruptAttachingToTangle, - InterruptAttachingToTangleCommand, - ) + with patch('iota.commands.core.interrupt_attaching_to_tangle.InterruptAttachingToTangleCommand.__call__', + MagicMock(return_value='You found me!') + ) as mocked_command: + + api = Iota(self.adapter) + + # Don't need to call with proper args here. + response = api.interrupt_attaching_to_tangle() + + self.assertTrue(mocked_command.called) + + self.assertEqual( + response, + 'You found me!' + ) diff --git a/test/commands/core/remove_neighbors_test.py b/test/commands/core/remove_neighbors_test.py index b05c532f..32ca749e 100644 --- a/test/commands/core/remove_neighbors_test.py +++ b/test/commands/core/remove_neighbors_test.py @@ -10,6 +10,7 @@ from iota.adapter import MockAdapter from iota.commands.core.remove_neighbors import RemoveNeighborsCommand from iota.filters import NodeUri +from test import patch, MagicMock class RemoveNeighborsRequestFilterTestCase(BaseFilterTestCase): @@ -153,8 +154,21 @@ def setUp(self): def test_wireup(self): """ Verify that the command is wired up correctly. + + The API method indeed calls the appropiate command. """ - self.assertIsInstance( - Iota(self.adapter).removeNeighbors, - RemoveNeighborsCommand, - ) + with patch('iota.commands.core.remove_neighbors.RemoveNeighborsCommand.__call__', + MagicMock(return_value='You found me!') + ) as mocked_command: + + api = Iota(self.adapter) + + # Don't need to call with proper args here. + response = api.remove_neighbors('uris') + + self.assertTrue(mocked_command.called) + + self.assertEqual( + response, + 'You found me!' + ) diff --git a/test/commands/core/store_transactions_test.py b/test/commands/core/store_transactions_test.py index 57e20457..b1a87d1f 100644 --- a/test/commands/core/store_transactions_test.py +++ b/test/commands/core/store_transactions_test.py @@ -12,6 +12,7 @@ from iota.adapter import MockAdapter from iota.commands.core.store_transactions import StoreTransactionsCommand from iota.filters import Trytes +from test import patch, MagicMock class StoreTransactionsRequestFilterTestCase(BaseFilterTestCase): @@ -187,8 +188,21 @@ def setUp(self): def test_wireup(self): """ Verify that the command is wired up correctly. + + The API method indeed calls the appropiate command. """ - self.assertIsInstance( - Iota(self.adapter).storeTransactions, - StoreTransactionsCommand, - ) + with patch('iota.commands.core.store_transactions.StoreTransactionsCommand.__call__', + MagicMock(return_value='You found me!') + ) as mocked_command: + + api = Iota(self.adapter) + + # Don't need to call with proper args here. + response = api.store_transactions('trytes') + + self.assertTrue(mocked_command.called) + + self.assertEqual( + response, + 'You found me!' + ) diff --git a/test/commands/core/were_addresses_spent_from_test.py b/test/commands/core/were_addresses_spent_from_test.py index 4fd482e1..399a197b 100644 --- a/test/commands/core/were_addresses_spent_from_test.py +++ b/test/commands/core/were_addresses_spent_from_test.py @@ -11,6 +11,7 @@ from iota.adapter import MockAdapter from iota.commands.core import WereAddressesSpentFromCommand from iota.filters import Trytes +from test import patch, MagicMock class WereAddressesSpentFromRequestFilterTestCase(BaseFilterTestCase): @@ -169,8 +170,21 @@ def setUp(self): def test_wireup(self): """ Verify that the command is wired up correctly. + + The API method indeed calls the appropiate command. """ - self.assertIsInstance( - Iota(self.adapter).wereAddressesSpentFrom, - WereAddressesSpentFromCommand, - ) + with patch('iota.commands.core.were_addresses_spent_from.WereAddressesSpentFromCommand.__call__', + MagicMock(return_value='You found me!') + ) as mocked_command: + + api = Iota(self.adapter) + + # Don't need to call with proper args here. + response = api.were_addresses_spent_from('addresses') + + self.assertTrue(mocked_command.called) + + self.assertEqual( + response, + 'You found me!' + ) diff --git a/test/commands/extended/broadcast_and_store_test.py b/test/commands/extended/broadcast_and_store_test.py index 6e02dd27..9354784e 100644 --- a/test/commands/extended/broadcast_and_store_test.py +++ b/test/commands/extended/broadcast_and_store_test.py @@ -9,6 +9,7 @@ from iota import Iota, TransactionTrytes from iota.adapter import MockAdapter from iota.commands.extended.broadcast_and_store import BroadcastAndStoreCommand +from test import patch, MagicMock class BroadcastAndStoreCommandTestCase(TestCase): @@ -27,11 +28,24 @@ def setUp(self): def test_wireup(self): """ Verify that the command is wired up correctly. + + The API method indeed calls the appropiate command. """ - self.assertIsInstance( - Iota(self.adapter).broadcastAndStore, - BroadcastAndStoreCommand, - ) + with patch('iota.commands.extended.broadcast_and_store.BroadcastAndStoreCommand.__call__', + MagicMock(return_value='You found me!') + ) as mocked_command: + + api = Iota(self.adapter) + + # Don't need to call with proper args here. + response = api.broadcast_and_store('trytes') + + self.assertTrue(mocked_command.called) + + self.assertEqual( + response, + 'You found me!' + ) def test_happy_path(self): """ diff --git a/test/commands/extended/broadcast_bundle_test.py b/test/commands/extended/broadcast_bundle_test.py index b6014ac7..3d047801 100644 --- a/test/commands/extended/broadcast_bundle_test.py +++ b/test/commands/extended/broadcast_bundle_test.py @@ -12,13 +12,8 @@ from iota.adapter import MockAdapter from iota.commands.extended.broadcast_bundle import BroadcastBundleCommand from iota.filters import Trytes +from test import patch, MagicMock -from six import PY2 - -if PY2: - from mock import MagicMock, patch -else: - from unittest.mock import MagicMock, patch # RequestFilterTestCase code reused from get_bundles_test.py class BroadcastBundleRequestFilterTestCase(BaseFilterTestCase): @@ -148,11 +143,24 @@ def setUp(self): def test_wireup(self): """ Verify that the command is wired up correctly. + + The API method indeed calls the appropiate command. """ - self.assertIsInstance( - Iota(self.adapter).broadcastBundle, - BroadcastBundleCommand, - ) + with patch('iota.commands.extended.broadcast_bundle.BroadcastBundleCommand.__call__', + MagicMock(return_value='You found me!') + ) as mocked_command: + + api = Iota(self.adapter) + + # Don't need to call with proper args here. + response = api.broadcast_bundle('trytes') + + self.assertTrue(mocked_command.called) + + self.assertEqual( + response, + 'You found me!' + ) def test_happy_path(self): """ diff --git a/test/commands/extended/find_transaction_objects.py b/test/commands/extended/find_transaction_objects.py index a98b97c7..e87b3805 100644 --- a/test/commands/extended/find_transaction_objects.py +++ b/test/commands/extended/find_transaction_objects.py @@ -4,10 +4,9 @@ from unittest import TestCase -import mock - from iota import Iota, MockAdapter, Transaction from iota.commands.extended import FindTransactionObjectsCommand +from test import patch, MagicMock, mock class FindTransactionObjectsCommandTestCase(TestCase): @@ -72,11 +71,24 @@ def setUp(self): def test_wireup(self): """ Verify that the command is wired up correctly. + + The API method indeed calls the appropiate command. """ - self.assertIsInstance( - Iota(self.adapter).findTransactionObjects, - FindTransactionObjectsCommand, - ) + with patch('iota.commands.extended.find_transaction_objects.FindTransactionObjectsCommand.__call__', + MagicMock(return_value='You found me!') + ) as mocked_command: + + api = Iota(self.adapter) + + # Don't need to call with proper args here. + response = api.find_transaction_objects('bundle') + + self.assertTrue(mocked_command.called) + + self.assertEqual( + response, + 'You found me!' + ) def test_transaction_found(self): """ diff --git a/test/commands/extended/get_account_data_test.py b/test/commands/extended/get_account_data_test.py index 649ac39f..1a0e524a 100644 --- a/test/commands/extended/get_account_data_test.py +++ b/test/commands/extended/get_account_data_test.py @@ -15,6 +15,7 @@ from iota.crypto.types import Seed from iota.filters import Trytes from test import mock +from test import patch, MagicMock class GetAccountDataRequestFilterTestCase(BaseFilterTestCase): @@ -362,11 +363,24 @@ def setUp(self): def test_wireup(self): """ Verify that the command is wired up correctly. + + The API method indeed calls the appropiate command. """ - self.assertIsInstance( - Iota(self.adapter).getAccountData, - GetAccountDataCommand, - ) + with patch('iota.commands.extended.get_account_data.GetAccountDataCommand.__call__', + MagicMock(return_value='You found me!') + ) as mocked_command: + + api = Iota(self.adapter) + + # Don't need to call with proper args here. + response = api.get_account_data() + + self.assertTrue(mocked_command.called) + + self.assertEqual( + response, + 'You found me!' + ) def test_happy_path(self): """ diff --git a/test/commands/extended/get_bundles_test.py b/test/commands/extended/get_bundles_test.py index 2a1de500..4d596fb4 100644 --- a/test/commands/extended/get_bundles_test.py +++ b/test/commands/extended/get_bundles_test.py @@ -12,6 +12,7 @@ from iota.adapter import MockAdapter from iota.commands.extended.get_bundles import GetBundlesCommand from iota.filters import Trytes +from test import patch, MagicMock class GetBundlesRequestFilterTestCase(BaseFilterTestCase): @@ -274,12 +275,25 @@ def setUp(self): def test_wireup(self): """ - Verifies that the command is wired up correctly. + Verify that the command is wired up correctly. + + The API method indeed calls the appropiate command. """ - self.assertIsInstance( - Iota(self.adapter).getBundles, - GetBundlesCommand, - ) + with patch('iota.commands.extended.get_bundles.GetBundlesCommand.__call__', + MagicMock(return_value='You found me!') + ) as mocked_command: + + api = Iota(self.adapter) + + # Don't need to call with proper args here. + response = api.get_bundles('transaction') + + self.assertTrue(mocked_command.called) + + self.assertEqual( + response, + 'You found me!' + ) def test_happy_path(self): """ diff --git a/test/commands/extended/get_inputs_test.py b/test/commands/extended/get_inputs_test.py index ee3594ab..439a687f 100644 --- a/test/commands/extended/get_inputs_test.py +++ b/test/commands/extended/get_inputs_test.py @@ -14,6 +14,7 @@ from iota.crypto.types import Seed from iota.filters import Trytes from test import mock +from test import patch, MagicMock class GetInputsRequestFilterTestCase(BaseFilterTestCase): @@ -441,11 +442,24 @@ def setUp(self): def test_wireup(self): """ Verify that the command is wired up correctly. + + The API method indeed calls the appropiate command. """ - self.assertIsInstance( - Iota(self.adapter).getInputs, - GetInputsCommand, - ) + with patch('iota.commands.extended.get_inputs.GetInputsCommand.__call__', + MagicMock(return_value='You found me!') + ) as mocked_command: + + api = Iota(self.adapter) + + # Don't need to call with proper args here. + response = api.get_inputs() + + self.assertTrue(mocked_command.called) + + self.assertEqual( + response, + 'You found me!' + ) def test_stop_threshold_met(self): """ diff --git a/test/commands/extended/get_latest_inclusion_test.py b/test/commands/extended/get_latest_inclusion_test.py index 375f631e..4680693d 100644 --- a/test/commands/extended/get_latest_inclusion_test.py +++ b/test/commands/extended/get_latest_inclusion_test.py @@ -12,6 +12,7 @@ from iota.commands.extended.get_latest_inclusion import \ GetLatestInclusionCommand from iota.filters import Trytes +from test import patch, MagicMock class GetLatestInclusionRequestFilterTestCase(BaseFilterTestCase): @@ -204,11 +205,24 @@ def setUp(self): def test_wireup(self): """ Verify that the command is wired up correctly. + + The API method indeed calls the appropiate command. """ - self.assertIsInstance( - Iota(self.adapter).getLatestInclusion, - GetLatestInclusionCommand, - ) + with patch('iota.commands.extended.get_latest_inclusion.GetLatestInclusionCommand.__call__', + MagicMock(return_value='You found me!') + ) as mocked_command: + + api = Iota(self.adapter) + + # Don't need to call with proper args here. + response = api.get_latest_inclusion('hashes') + + self.assertTrue(mocked_command.called) + + self.assertEqual( + response, + 'You found me!' + ) def test_happy_path(self): """ diff --git a/test/commands/extended/get_new_addresses_test.py b/test/commands/extended/get_new_addresses_test.py index 53730e78..bf14b0a1 100644 --- a/test/commands/extended/get_new_addresses_test.py +++ b/test/commands/extended/get_new_addresses_test.py @@ -13,6 +13,7 @@ from iota.crypto.addresses import AddressGenerator from iota.crypto.types import Seed from iota.filters import Trytes +from test import patch, MagicMock class GetNewAddressesRequestFilterTestCase(BaseFilterTestCase): @@ -365,11 +366,24 @@ def setUp(self): def test_wireup(self): """ Verify that the command is wired up correctly. + + The API method indeed calls the appropiate command. """ - self.assertIsInstance( - Iota(self.adapter).getNewAddresses, - GetNewAddressesCommand, - ) + with patch('iota.commands.extended.get_new_addresses.GetNewAddressesCommand.__call__', + MagicMock(return_value='You found me!') + ) as mocked_command: + + api = Iota(self.adapter) + + # Don't need to call with proper args here. + response = api.get_new_addresses('hashes') + + self.assertTrue(mocked_command.called) + + self.assertEqual( + response, + 'You found me!' + ) def test_get_addresses_offline(self): """ diff --git a/test/commands/extended/get_transaction_objects_test.py b/test/commands/extended/get_transaction_objects_test.py index 16fddbce..552889a4 100644 --- a/test/commands/extended/get_transaction_objects_test.py +++ b/test/commands/extended/get_transaction_objects_test.py @@ -4,10 +4,9 @@ from unittest import TestCase -import mock - from iota import Iota, MockAdapter, Transaction from iota.commands.extended import GetTransactionObjectsCommand +from test import patch, MagicMock, mock class GetTransactionObjectsCommandTestCase(TestCase): @@ -71,11 +70,24 @@ def setUp(self): def test_wireup(self): """ Verify that the command is wired up correctly. + + The API method indeed calls the appropiate command. """ - self.assertIsInstance( - Iota(self.adapter).getTransactionObjects, - GetTransactionObjectsCommand, - ) + with patch('iota.commands.extended.get_transaction_objects.GetTransactionObjectsCommand.__call__', + MagicMock(return_value='You found me!') + ) as mocked_command: + + api = Iota(self.adapter) + + # Don't need to call with proper args here. + response = api.get_transaction_objects('hashes') + + self.assertTrue(mocked_command.called) + + self.assertEqual( + response, + 'You found me!' + ) def test_transaction_found(self): """ diff --git a/test/commands/extended/get_transfers_test.py b/test/commands/extended/get_transfers_test.py index 4b64d707..7373daf4 100644 --- a/test/commands/extended/get_transfers_test.py +++ b/test/commands/extended/get_transfers_test.py @@ -15,6 +15,7 @@ from iota.crypto.types import Seed from iota.filters import Trytes from test import mock +from test import patch, MagicMock class GetTransfersRequestFilterTestCase(BaseFilterTestCase): @@ -339,12 +340,24 @@ def setUp(self): def test_wireup(self): """ Verify that the command is wired up correctly. + + The API method indeed calls the appropiate command. """ - self.assertIsInstance( - Iota(self.adapter).getTransfers, - GetTransfersCommand, - ) + with patch('iota.commands.extended.get_transfers.GetTransfersCommand.__call__', + MagicMock(return_value='You found me!') + ) as mocked_command: + + api = Iota(self.adapter) + # Don't need to call with proper args here. + response = api.get_transfers() + + self.assertTrue(mocked_command.called) + + self.assertEqual( + response, + 'You found me!' + ) def test_full_scan(self): """ Scanning the Tangle for all transfers. diff --git a/test/commands/extended/is_promotable_test.py b/test/commands/extended/is_promotable_test.py index ad991789..cc1506a3 100644 --- a/test/commands/extended/is_promotable_test.py +++ b/test/commands/extended/is_promotable_test.py @@ -14,6 +14,7 @@ get_current_ms, is_within_depth, MILESTONE_INTERVAL, ONE_WAY_DELAY from iota.filters import Trytes from test import mock +from test import patch, MagicMock class IsPromotableRequestFilterTestCase(BaseFilterTestCase): filter_type = IsPromotableCommand(MockAdapter()).get_request_filter @@ -290,11 +291,24 @@ def setUp(self): def test_wireup(self): """ Verify that the command is wired up correctly. + + The API method indeed calls the appropiate command. """ - self.assertIsInstance( - Iota(self.adapter).isPromotable, - IsPromotableCommand, - ) + with patch('iota.commands.extended.is_promotable.IsPromotableCommand.__call__', + MagicMock(return_value='You found me!') + ) as mocked_command: + + api = Iota(self.adapter) + + # Don't need to call with proper args here. + response = api.is_promotable('tails') + + self.assertTrue(mocked_command.called) + + self.assertEqual( + response, + 'You found me!' + ) def test_happy_path(self): """ diff --git a/test/commands/extended/is_reattachable_test.py b/test/commands/extended/is_reattachable_test.py index edef5fd5..2800b069 100644 --- a/test/commands/extended/is_reattachable_test.py +++ b/test/commands/extended/is_reattachable_test.py @@ -11,6 +11,7 @@ from iota import Address, Iota from iota.adapter import MockAdapter from iota.commands.extended.is_reattachable import IsReattachableCommand +from test import patch, MagicMock class IsReattachableRequestFilterTestCase(BaseFilterTestCase): @@ -199,8 +200,21 @@ def setUp(self): def test_wireup(self): """ Verify that the command is wired up correctly. + + The API method indeed calls the appropiate command. """ - self.assertIsInstance( - Iota(self.adapter).isReattachable, - IsReattachableCommand, - ) + with patch('iota.commands.extended.is_reattachable.IsReattachableCommand.__call__', + MagicMock(return_value='You found me!') + ) as mocked_command: + + api = Iota(self.adapter) + + # Don't need to call with proper args here. + response = api.is_reattachable('addresses') + + self.assertTrue(mocked_command.called) + + self.assertEqual( + response, + 'You found me!' + ) diff --git a/test/commands/extended/prepare_transfer_test.py b/test/commands/extended/prepare_transfer_test.py index 35e0f9df..0a6c46b6 100644 --- a/test/commands/extended/prepare_transfer_test.py +++ b/test/commands/extended/prepare_transfer_test.py @@ -16,6 +16,7 @@ from iota.crypto.types import Seed from iota.filters import GeneratedAddress, Trytes from test import mock +from test import patch, MagicMock class PrepareTransferRequestFilterTestCase(BaseFilterTestCase): @@ -576,11 +577,24 @@ def get_current_timestamp(): def test_wireup(self): """ Verify that the command is wired up correctly. + + The API method indeed calls the appropiate command. """ - self.assertIsInstance( - Iota(self.adapter).prepareTransfer, - PrepareTransferCommand, - ) + with patch('iota.commands.extended.prepare_transfer.PrepareTransferCommand.__call__', + MagicMock(return_value='You found me!') + ) as mocked_command: + + api = Iota(self.adapter) + + # Don't need to call with proper args here. + response = api.prepare_transfer('transfers') + + self.assertTrue(mocked_command.called) + + self.assertEqual( + response, + 'You found me!' + ) def test_pass_inputs_not_needed(self): """ diff --git a/test/commands/extended/promote_transaction_test.py b/test/commands/extended/promote_transaction_test.py index 21f5b815..e732ae4f 100644 --- a/test/commands/extended/promote_transaction_test.py +++ b/test/commands/extended/promote_transaction_test.py @@ -13,6 +13,7 @@ from iota.commands.extended.promote_transaction import PromoteTransactionCommand from iota.filters import Trytes from test import mock +from test import patch, MagicMock class PromoteTransactionRequestFilterTestCase(BaseFilterTestCase): @@ -311,12 +312,25 @@ def setUp(self): def test_wireup(self): """ - Verifies that the command is wired-up correctly. + Verify that the command is wired up correctly. + + The API method indeed calls the appropiate command. """ - self.assertIsInstance( - Iota(self.adapter).promoteTransaction, - PromoteTransactionCommand, - ) + with patch('iota.commands.extended.promote_transaction.PromoteTransactionCommand.__call__', + MagicMock(return_value='You found me!') + ) as mocked_command: + + api = Iota(self.adapter) + + # Don't need to call with proper args here. + response = api.promote_transaction('transaction') + + self.assertTrue(mocked_command.called) + + self.assertEqual( + response, + 'You found me!' + ) def test_happy_path(self): """ diff --git a/test/commands/extended/replay_bundle_test.py b/test/commands/extended/replay_bundle_test.py index b6079bfd..858e038b 100644 --- a/test/commands/extended/replay_bundle_test.py +++ b/test/commands/extended/replay_bundle_test.py @@ -14,6 +14,7 @@ from iota.commands.extended.replay_bundle import ReplayBundleCommand from iota.filters import Trytes from test import mock +from test import patch, MagicMock class ReplayBundleRequestFilterTestCase(BaseFilterTestCase): @@ -303,12 +304,25 @@ def setUp(self): def test_wireup(self): """ - Verifies that the command is wired-up correctly. + Verify that the command is wired up correctly. + + The API method indeed calls the appropiate command. """ - self.assertIsInstance( - Iota(self.adapter).replayBundle, - ReplayBundleCommand, - ) + with patch('iota.commands.extended.replay_bundle.ReplayBundleCommand.__call__', + MagicMock(return_value='You found me!') + ) as mocked_command: + + api = Iota(self.adapter) + + # Don't need to call with proper args here. + response = api.replay_bundle('transaction') + + self.assertTrue(mocked_command.called) + + self.assertEqual( + response, + 'You found me!' + ) def test_happy_path(self): """ diff --git a/test/commands/extended/send_transfer_test.py b/test/commands/extended/send_transfer_test.py index aaedac8d..8a277c0e 100644 --- a/test/commands/extended/send_transfer_test.py +++ b/test/commands/extended/send_transfer_test.py @@ -16,6 +16,7 @@ from iota.crypto.types import Seed from iota.filters import Trytes from test import mock +from test import patch, MagicMock class SendTransferRequestFilterTestCase(BaseFilterTestCase): @@ -671,12 +672,25 @@ def setUp(self): def test_wireup(self): """ - Verifies that the command is wired up correctly. + Verify that the command is wired up correctly. + + The API method indeed calls the appropiate command. """ - self.assertIsInstance( - Iota(self.adapter).sendTransfer, - SendTransferCommand, - ) + with patch('iota.commands.extended.send_transfer.SendTransferCommand.__call__', + MagicMock(return_value='You found me!') + ) as mocked_command: + + api = Iota(self.adapter) + + # Don't need to call with proper args here. + response = api.send_transfer('transfers') + + self.assertTrue(mocked_command.called) + + self.assertEqual( + response, + 'You found me!' + ) def test_happy_path(self): """ diff --git a/test/commands/extended/send_trytes_test.py b/test/commands/extended/send_trytes_test.py index b2d4d02e..70301806 100644 --- a/test/commands/extended/send_trytes_test.py +++ b/test/commands/extended/send_trytes_test.py @@ -12,6 +12,7 @@ from iota.adapter import MockAdapter from iota.commands.extended.send_trytes import SendTrytesCommand from iota.filters import Trytes +from test import patch, MagicMock class SendTrytesRequestFilterTestCase(BaseFilterTestCase): @@ -371,11 +372,24 @@ def setUp(self): def test_wireup(self): """ Verify that the command is wired up correctly. + + The API method indeed calls the appropiate command. """ - self.assertIsInstance( - Iota(self.adapter).sendTrytes, - SendTrytesCommand, - ) + with patch('iota.commands.extended.send_trytes.SendTrytesCommand.__call__', + MagicMock(return_value='You found me!') + ) as mocked_command: + + api = Iota(self.adapter) + + # Don't need to call with proper args here. + response = api.send_trytes('trytes') + + self.assertTrue(mocked_command.called) + + self.assertEqual( + response, + 'You found me!' + ) def test_happy_path(self): """ diff --git a/test/commands/extended/traverse_bundle_test.py b/test/commands/extended/traverse_bundle_test.py index e700c776..f28066fd 100644 --- a/test/commands/extended/traverse_bundle_test.py +++ b/test/commands/extended/traverse_bundle_test.py @@ -12,7 +12,7 @@ from iota.adapter import MockAdapter from iota.commands.extended.traverse_bundle import TraverseBundleCommand from iota.filters import Trytes - +from test import patch, MagicMock # Same tests as for GetBundlesRequestFilter (it is the same filter) class TraverseBundleRequestFilterTestCase(BaseFilterTestCase): @@ -129,12 +129,25 @@ def setUp(self): def test_wireup(self): """ - Verifies that the command is wired up correctly. + Verify that the command is wired up correctly. + + The API method indeed calls the appropiate command. """ - self.assertIsInstance( - Iota(self.adapter).traverseBundle, - TraverseBundleCommand, - ) + with patch('iota.commands.extended.traverse_bundle.TraverseBundleCommand.__call__', + MagicMock(return_value='You found me!') + ) as mocked_command: + + api = Iota(self.adapter) + + # Don't need to call with proper args here. + response = api.traverse_bundle('tail') + + self.assertTrue(mocked_command.called) + + self.assertEqual( + response, + 'You found me!' + ) def test_single_transaction(self): """ diff --git a/test/multisig/commands/create_multisig_address_test.py b/test/multisig/commands/create_multisig_address_test.py index 301fd9eb..be7f3adc 100644 --- a/test/multisig/commands/create_multisig_address_test.py +++ b/test/multisig/commands/create_multisig_address_test.py @@ -15,6 +15,7 @@ from iota.multisig import MultisigIota from iota.multisig.commands import CreateMultisigAddressCommand from iota.multisig.types import MultisigAddress +from test import patch, MagicMock class CreateMultisigAddressCommandTestCase(TestCase): @@ -49,11 +50,24 @@ def setUp(self): def test_wireup(self): """ Verify that the command is wired up correctly. + + The API method indeed calls the appropiate command. """ - self.assertIsInstance( - MultisigIota(self.adapter).createMultisigAddress, - CreateMultisigAddressCommand, - ) + with patch('iota.multisig.commands.create_multisig_address.CreateMultisigAddressCommand.__call__', + MagicMock(return_value='You found me!') + ) as mocked_command: + + api = MultisigIota(self.adapter) + + # Don't need to call with proper args here. + response = api.create_multisig_address('digests') + + self.assertTrue(mocked_command.called) + + self.assertEqual( + response, + 'You found me!' + ) def test_happy_path(self): """ diff --git a/test/multisig/commands/get_digests_test.py b/test/multisig/commands/get_digests_test.py index fa96c477..daa88c20 100644 --- a/test/multisig/commands/get_digests_test.py +++ b/test/multisig/commands/get_digests_test.py @@ -16,7 +16,7 @@ from iota.filters import Trytes from iota.multisig import MultisigIota from iota.multisig.commands import GetDigestsCommand -from test import mock +from test import mock, patch, MagicMock class GetDigestsCommandTestCase(TestCase): @@ -37,11 +37,24 @@ def setUp(self): def test_wireup(self): """ Verify that the command is wired up correctly. + + The API method indeed calls the appropiate command. """ - self.assertIsInstance( - MultisigIota(self.adapter).getDigests, - GetDigestsCommand, - ) + with patch('iota.multisig.commands.get_digests.GetDigestsCommand.__call__', + MagicMock(return_value='You found me!') + ) as mocked_command: + + api = MultisigIota(self.adapter) + + # Don't need to call with proper args here. + response = api.get_digests() + + self.assertTrue(mocked_command.called) + + self.assertEqual( + response, + 'You found me!' + ) def test_generate_single_digest(self): """ diff --git a/test/multisig/commands/get_private_keys_test.py b/test/multisig/commands/get_private_keys_test.py index c68d5fb4..a6dc7d54 100644 --- a/test/multisig/commands/get_private_keys_test.py +++ b/test/multisig/commands/get_private_keys_test.py @@ -16,7 +16,7 @@ from iota.filters import Trytes from iota.multisig import MultisigIota from iota.multisig.commands import GetPrivateKeysCommand -from test import mock +from test import mock, patch, MagicMock class GetPrivateKeysCommandTestCase(TestCase): @@ -41,11 +41,24 @@ def setUp(self): def test_wireup(self): """ Verify that the command is wired up correctly. + + The API method indeed calls the appropiate command. """ - self.assertIsInstance( - MultisigIota(self.adapter).getPrivateKeys, - GetPrivateKeysCommand, - ) + with patch('iota.multisig.commands.get_private_keys.GetPrivateKeysCommand.__call__', + MagicMock(return_value='You found me!') + ) as mocked_command: + + api = MultisigIota(self.adapter) + + # Don't need to call with proper args here. + response = api.get_private_keys() + + self.assertTrue(mocked_command.called) + + self.assertEqual( + response, + 'You found me!' + ) def test_generate_single_key(self): """ diff --git a/test/multisig/commands/prepare_multisig_transfer_test.py b/test/multisig/commands/prepare_multisig_transfer_test.py index 9cb01990..bbf4ac6b 100644 --- a/test/multisig/commands/prepare_multisig_transfer_test.py +++ b/test/multisig/commands/prepare_multisig_transfer_test.py @@ -14,6 +14,7 @@ from iota.multisig import MultisigIota from iota.multisig.commands import PrepareMultisigTransferCommand from iota.multisig.types import MultisigAddress +from test import patch, MagicMock class PrepareMultisigTransferRequestFilterTestCase(BaseFilterTestCase): @@ -526,12 +527,25 @@ def setUp(self): def test_wireup(self): """ - Verifies the command is wired up correctly. + Verify that the command is wired up correctly. + + The API method indeed calls the appropiate command. """ - self.assertIsInstance( - MultisigIota(self.adapter).prepareMultisigTransfer, - PrepareMultisigTransferCommand, - ) + with patch('iota.multisig.commands.prepare_multisig_transfer.PrepareMultisigTransferCommand.__call__', + MagicMock(return_value='You found me!') + ) as mocked_command: + + api = MultisigIota(self.adapter) + + # Don't need to call with proper args here. + response = api.prepare_multisig_transfer('transfer', 'multisig_input') + + self.assertTrue(mocked_command.called) + + self.assertEqual( + response, + 'You found me!' + ) def test_happy_path(self): """