This repository was archived by the owner on Jan 13, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 126
Implementation of IsReattachableCommand #108
Merged
todofixthis
merged 8 commits into
iotaledger-archive:develop
from
jinnerbichler:38-is_reattachable
Jan 14, 2018
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
db1bfef
added empty command class
jinnerbichler a4b6006
worked on first iteration of unit tests
jinnerbichler 573c4f4
worked on execution of isReattachable
jinnerbichler 14c6854
finished first version of execution
jinnerbichler e05083f
removed import
jinnerbichler 35b6325
changed order of imports
jinnerbichler 0b757c8
removed test for single address for now
jinnerbichler 9311bd1
added test for single address
jinnerbichler File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| # coding=utf-8 | ||
| from __future__ import absolute_import, division, print_function, unicode_literals | ||
| from typing import List | ||
|
|
||
| import filters as f | ||
|
|
||
| from iota import Address | ||
| from iota.commands import FilterCommand, RequestFilter, ResponseFilter | ||
| from iota.commands.extended import GetLatestInclusionCommand | ||
| from iota.commands.extended.utils import find_transaction_objects | ||
| from iota.filters import Trytes | ||
|
|
||
| __all__ = [ | ||
| 'IsReattachableCommand', | ||
| ] | ||
|
|
||
|
|
||
| class IsReattachableCommand(FilterCommand): | ||
| """ | ||
| Executes ``isReattachable`` extended API command. | ||
| """ | ||
| command = 'isReattachable' | ||
|
|
||
| def get_request_filter(self): | ||
| return IsReattachableRequestFilter() | ||
|
|
||
| def get_response_filter(self): | ||
| return IsReattachableResponseFilter() | ||
|
|
||
| def _execute(self, request): | ||
| addresses = request['addresses'] # type: List[Address] | ||
|
|
||
| # fetch full transaction objects | ||
| transactions = find_transaction_objects(adapter=self.adapter, **{'addresses': addresses}) | ||
|
|
||
| # map and filter transactions, which have zero value. | ||
| # If multiple transactions for the same address are returned the one with the | ||
| # highest attachment_timestamp is selected | ||
| transactions = sorted(transactions, key=lambda t: t.attachment_timestamp) | ||
| transaction_map = {t.address: t.hash for t in transactions if t.value > 0} | ||
|
|
||
| # fetch inclusion states | ||
| inclusion_states = GetLatestInclusionCommand(adapter=self.adapter)(hashes=list(transaction_map.values())) | ||
| inclusion_states = inclusion_states['states'] | ||
|
|
||
| return { | ||
| 'reattachable': [not inclusion_states[transaction_map[address]] for address in addresses] | ||
| } | ||
|
|
||
|
|
||
| class IsReattachableRequestFilter(RequestFilter): | ||
| def __init__(self): | ||
| super(IsReattachableRequestFilter, self).__init__( | ||
| { | ||
| 'addresses': ( | ||
| f.Required | ||
| | f.Array | ||
| | f.FilterRepeater( | ||
| f.Required | ||
| | Trytes(result_type=Address) | ||
| | f.Unicode(encoding='ascii', normalize=False) | ||
| ) | ||
| ) | ||
| } | ||
| ) | ||
|
|
||
|
|
||
| class IsReattachableResponseFilter(ResponseFilter): | ||
| def __init__(self): | ||
| super(IsReattachableResponseFilter, self).__init__({ | ||
| 'reattachable': ( | ||
| f.Required | ||
| | f.Array | ||
| | f.FilterRepeater(f.Type(bool))) | ||
| }) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,206 @@ | ||
| # coding=utf-8 | ||
| from __future__ import absolute_import, division, print_function, \ | ||
| unicode_literals | ||
|
|
||
| from unittest import TestCase | ||
|
|
||
| import filters as f | ||
| from filters.test import BaseFilterTestCase | ||
| from six import text_type | ||
|
|
||
| from iota import Address, Iota | ||
| from iota.adapter import MockAdapter | ||
| from iota.commands.extended.is_reattachable import IsReattachableCommand | ||
|
|
||
|
|
||
| class IsReattachableRequestFilterTestCase(BaseFilterTestCase): | ||
| filter_type = IsReattachableCommand(MockAdapter()).get_request_filter | ||
| skip_value_check = True | ||
|
|
||
| # noinspection SpellCheckingInspection | ||
| def setUp(self): | ||
| super(IsReattachableRequestFilterTestCase, self).setUp() | ||
|
|
||
| # Define a few valid values that we can reuse across tests. | ||
| self.address_1 = ( | ||
| 'TESTVALUE9DONTUSEINPRODUCTION99999EKJZZT' | ||
| 'SOGJOUNVEWLDPKGTGAOIZIPMGBLHC9LMQNHLGXGYX' | ||
| ) | ||
|
|
||
| self.address_2 = ( | ||
| 'TESTVALUE9DONTUSEINPRODUCTION99999FDCDTZ' | ||
| 'ZWLL9MYGUTLSYVSIFJ9NGALTRMCQVIIOVEQOITYTE' | ||
| ) | ||
|
|
||
| def test_pass_happy_path(self): | ||
| """ | ||
| Filter for list of valid string addresses | ||
| """ | ||
|
|
||
| request = { | ||
| # Raw trytes are extracted to match the IRI's JSON protocol. | ||
| 'addresses': [ | ||
| self.address_1, | ||
| Address(self.address_2) | ||
| ], | ||
| } | ||
|
|
||
| filter_ = self._filter(request) | ||
|
|
||
| self.assertFilterPasses(filter_) | ||
|
|
||
| self.assertDictEqual( | ||
| filter_.cleaned_data, | ||
| { | ||
| 'addresses': [ | ||
| text_type(Address(self.address_1)), | ||
| text_type(Address(self.address_2)) | ||
| ], | ||
| }, | ||
| ) | ||
|
|
||
| def test_pass_compatible_types(self): | ||
| """ | ||
| The incoming request contains values that can be converted to the | ||
| expected types. | ||
| """ | ||
| request = { | ||
| 'addresses': [ | ||
| Address(self.address_1), | ||
| bytearray(self.address_2.encode('ascii')), | ||
| ], | ||
| } | ||
|
|
||
| filter_ = self._filter(request) | ||
|
|
||
| self.assertFilterPasses(filter_) | ||
| self.assertDictEqual( | ||
| filter_.cleaned_data, | ||
| { | ||
| 'addresses': [self.address_1, self.address_2], | ||
| }, | ||
| ) | ||
|
|
||
| def test_pass_incompatible_types(self): | ||
| """ | ||
| The incoming request contains values that can NOT be converted to the | ||
| expected types. | ||
| """ | ||
| request = { | ||
| 'addresses': [ | ||
| 1234234, | ||
| False | ||
| ], | ||
| } | ||
|
|
||
| self.assertFilterErrors( | ||
| request, | ||
| { | ||
| 'addresses.0': [f.Type.CODE_WRONG_TYPE], | ||
| 'addresses.1': [f.Type.CODE_WRONG_TYPE] | ||
| }, | ||
| ) | ||
|
|
||
| def test_fail_empty(self): | ||
| """ | ||
| The incoming request is empty. | ||
| """ | ||
| self.assertFilterErrors( | ||
| {}, | ||
| { | ||
| 'addresses': [f.FilterMapper.CODE_MISSING_KEY], | ||
| }, | ||
| ) | ||
|
|
||
| def test_fail_single_address(self): | ||
| """ | ||
| The incoming request contains a single address | ||
| """ | ||
| request = { | ||
| 'addresses': Address(self.address_1) | ||
| } | ||
|
|
||
| self.assertFilterErrors( | ||
| request, | ||
| { | ||
| 'addresses': [f.Type.CODE_WRONG_TYPE], | ||
| } | ||
| ) | ||
|
|
||
|
|
||
| # noinspection SpellCheckingInspection | ||
| class IsReattachableResponseFilterTestCase(BaseFilterTestCase): | ||
| filter_type = IsReattachableCommand(MockAdapter()).get_response_filter | ||
| skip_value_check = True | ||
|
|
||
| # noinspection SpellCheckingInspection | ||
| def setUp(self): | ||
| super(IsReattachableResponseFilterTestCase, self).setUp() | ||
|
|
||
| # Define a few valid values that we can reuse across tests. | ||
| self.addresses_1 = ( | ||
| 'TESTVALUE9DONTUSEINPRODUCTION99999EKJZZT' | ||
| 'SOGJOUNVEWLDPKGTGAOIZIPMGBLHC9LMQNHLGXGYX' | ||
| ) | ||
|
|
||
| def test_pass_happy_path(self): | ||
| """ | ||
| Typical ``IsReattachable`` request. | ||
| """ | ||
| response = { | ||
| 'reattachable': [True, False] | ||
| } | ||
|
|
||
| filter_ = self._filter(response) | ||
|
|
||
| self.assertFilterPasses(filter_) | ||
| self.assertDictEqual(filter_.cleaned_data, response) | ||
|
|
||
| def test_fail_empty(self): | ||
| """ | ||
| The incoming response is empty. | ||
| """ | ||
| self.assertFilterErrors( | ||
| {}, | ||
| { | ||
| 'reattachable': [f.Required.CODE_EMPTY], | ||
| }, | ||
| ) | ||
|
|
||
| def test_pass_incompatible_types(self): | ||
| """ | ||
| The response contains values that can NOT be converted to the | ||
| expected types. | ||
| """ | ||
| request = { | ||
| 'reattachable': [ | ||
| 1234234, | ||
| b'', | ||
| 'test' | ||
| ], | ||
| } | ||
|
|
||
| self.assertFilterErrors( | ||
| request, | ||
| { | ||
| 'reattachable.0': [f.Type.CODE_WRONG_TYPE], | ||
| 'reattachable.1': [f.Type.CODE_WRONG_TYPE], | ||
| 'reattachable.2': [f.Type.CODE_WRONG_TYPE] | ||
| }, | ||
| ) | ||
|
|
||
|
|
||
| class IsReattachableCommandTestCase(TestCase): | ||
| def setUp(self): | ||
| super(IsReattachableCommandTestCase, self).setUp() | ||
|
|
||
| self.adapter = MockAdapter() | ||
|
|
||
| def test_wireup(self): | ||
| """ | ||
| Verify that the command is wired up correctly. | ||
| """ | ||
| self.assertIsInstance( | ||
| Iota(self.adapter).isReattachable, | ||
| IsReattachableCommand, | ||
| ) | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we include a test for
addressesnot an array (e.g., callingis_reattachable(Address(...)))?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How is that possible with filters (https://filters.readthedocs.io/en/latest/#)?
I couldn't find a proper configuration for accepting both, an
AddressOR an array containingAddressobjects?That's how validation done at the time beeing:
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry; that was irresponsible wording on my part — if
addressesis not an array, the filter should reject it.Just for the sake of coverage — if we were to remove
f.Arrayfrom that filter, I don't think there are any unit tests that would fail as a result.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
np. Added this test case