diff --git a/setup.py b/setup.py index 8fc5952ae5d8bedd8515f547ed262cb4345ae570..0d8638e57671bb51b3f6765a54a29e4eae95becc 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,6 @@ from PyQt5 import QtCore ############################################################################# # preparation des options -sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), 'lib'))) sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), 'src'))) print(sys.path) @@ -31,7 +30,7 @@ if sys.platform == "win32": libEGL_path = os.path.join(os.path.dirname(path), "libEGL.dll") if 'CONDA_ENV_PATH' in os.environ: - # Check if we are in Conda env + # Check if we are in Conda env path = QtCore.QCoreApplication.libraryPaths()[0] libEGL_path = os.path.join(path, "Scripts", "libEGL.dll") libsodium_path = os.path.join(path, "Scripts", "libsodium.dll") @@ -87,7 +86,7 @@ target = Executable( # creation du setup setup( name = "cutecoin", - version = "0.10", + version = "0.11", description = "UCoin client", author = "Inso", options = {"build_exe": options}, diff --git a/src/cutecoin/core/registry/identity.py b/src/cutecoin/core/registry/identity.py index b7676750168f3db9405748a6af78e94e21227930..3ee79f8cd4c903580c463c1e9cf7057fddd54907 100644 --- a/src/cutecoin/core/registry/identity.py +++ b/src/cutecoin/core/registry/identity.py @@ -171,11 +171,13 @@ class Identity(QObject): :param cutecoin.core.community.Community community: The community target to request the join date :return: The membership data in BMA json format + :rtype: dict """ try: search = yield from community.bma_access.future_request(bma.blockchain.Membership, {'search': self.pubkey}) block_number = -1 + membership_data = None for ms in search['memberships']: if ms['blockNumber'] > block_number: block_number = ms['blockNumber'] @@ -184,13 +186,19 @@ class Identity(QObject): membership_data = ms else: membership_data = ms - return membership_data + if membership_data: + return membership_data + else: + raise MembershipNotFoundError(self.pubkey, community.name) + except ValueError as e: - if '404' in str(e) or '400' in str(e): + if '404' in str(e) or '400' in str(e): raise MembershipNotFoundError(self.pubkey, community.name) except NoPeerAvailable as e: logging.debug(str(e)) raise MembershipNotFoundError(self.pubkey, community.name) + except UnboundLocalError: + raise @asyncio.coroutine def published_uid(self, community): diff --git a/src/cutecoin/gui/identities_tab.py b/src/cutecoin/gui/identities_tab.py index c54fe9bc26366eeebc1c1a947fd30b9fee58f81d..8495167afb83eff3d1bbb05d60ed6eb19e041e3b 100644 --- a/src/cutecoin/gui/identities_tab.py +++ b/src/cutecoin/gui/identities_tab.py @@ -193,7 +193,7 @@ class IdentitiesTabWidget(QWidget, Ui_IdentitiesTab): identities.append(identity) self.edit_textsearch.clear() - self.refresh_identities(identities) + yield from self.refresh_identities(identities) self.busy.hide() @once_at_a_time @@ -212,7 +212,7 @@ class IdentitiesTabWidget(QWidget, Ui_IdentitiesTab): identities.append(identity) self.edit_textsearch.clear() - self.refresh_identities(identities) + yield from self.refresh_identities(identities) self.busy.hide() @once_at_a_time @@ -224,8 +224,8 @@ class IdentitiesTabWidget(QWidget, Ui_IdentitiesTab): """ if self.account and self.community: try: + yield from self.refresh_identities([]) self.busy.show() - self.refresh_identities([]) self_identity = yield from self.account.identity(self.community) account_connections = [] certs_of = yield from self_identity.unique_valid_certifiers_of(self.app.identities_registry, self.community) @@ -238,24 +238,19 @@ class IdentitiesTabWidget(QWidget, Ui_IdentitiesTab): certified_by = [p for p in account_connections if p.pubkey not in [i.pubkey for i in certifiers_of]] identities = certifiers_of + certified_by - self.refresh_identities(identities) + self.busy.hide() + yield from self.refresh_identities(identities) except NoPeerAvailable: - pass - finally: self.busy.hide() - @once_at_a_time - @asyncify @asyncio.coroutine def refresh_identities(self, identities): """ Refresh the table with specified identities. If no identities is passed, use the account connections. """ - self.busy.show() yield from self.table_identities.model().sourceModel().refresh_identities(identities) self.table_identities.resizeColumnsToContents() - self.busy.hide() def resizeEvent(self, event): self.busy.resize(event.size()) diff --git a/src/cutecoin/main.py b/src/cutecoin/main.py index fec1fbbbdd19ad8d3973a8ff365df2effba3845e..5145e7bd740ca26e37eca3a9d9391143edf68849 100755 --- a/src/cutecoin/main.py +++ b/src/cutecoin/main.py @@ -41,7 +41,8 @@ def async_exception_handler(loop, context): log_lines.append('{}: {!r}'.format(key, context[key])) logging.error('\n'.join(log_lines), exc_info=exc_info) - os._exit(1) + if "Unclosed" not in message: + os._exit(1) if __name__ == '__main__': diff --git a/src/cutecoin/tests/core/account.py b/src/cutecoin/tests/core/test_account.py similarity index 100% rename from src/cutecoin/tests/core/account.py rename to src/cutecoin/tests/core/test_account.py diff --git a/src/cutecoin/tests/core/community.py b/src/cutecoin/tests/core/test_community.py similarity index 100% rename from src/cutecoin/tests/core/community.py rename to src/cutecoin/tests/core/test_community.py diff --git a/src/cutecoin/tests/core/test_identity.py b/src/cutecoin/tests/core/test_identity.py new file mode 100644 index 0000000000000000000000000000000000000000..13be206691f3e44c9797208efd9c21b45e7f43f7 --- /dev/null +++ b/src/cutecoin/tests/core/test_identity.py @@ -0,0 +1,85 @@ +import sys +import unittest +import asyncio +import quamash +import logging +import time +from PyQt5.QtCore import QLocale +from cutecoin.core.registry.identities import Identity, IdentitiesRegistry, LocalState, BlockchainState +from cutecoin.tests.mocks.monkeypatch import pretender_reversed +from cutecoin.tests.mocks.bma import nice_blockchain, corrupted +from cutecoin.tests import get_application +from cutecoin.core import Application, Community +from cutecoin.core.net import Network, Node +from ucoinpy.documents.peer import BMAEndpoint +from cutecoin.core.net.api.bma.access import BmaAccess +from cutecoin.tools.exceptions import MembershipNotFoundError +from ucoinpy.api.bma import API + + +class TestIdentity(unittest.TestCase): + def setUp(self): + self.qapplication = get_application() + QLocale.setDefault(QLocale("en_GB")) + self.lp = quamash.QEventLoop(self.qapplication) + asyncio.set_event_loop(self.lp) + self.identities_registry = IdentitiesRegistry() + + self.application = Application(self.qapplication, self.lp, self.identities_registry) + self.application.preferences['notifications'] = False + + self.endpoint = BMAEndpoint("", "127.0.0.1", "", 50000) + self.node = Node("test_currency", [self.endpoint], + "", "HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk", + None, Node.ONLINE, + time.time(), {}, "ucoin", "0.12.0", 0) + self.network = Network.create(self.node) + self.bma_access = BmaAccess.create(self.network) + self.community = Community("test_currency", self.network, self.bma_access) + + def tearDown(self): + try: + self.lp.close() + finally: + asyncio.set_event_loop(None) + + def test_identity_membership(self): + mock = nice_blockchain.get_mock() + time.sleep(2) + logging.debug(mock.pretend_url) + API.reverse_url = pretender_reversed(mock.pretend_url) + identity = Identity("john", "7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ", + LocalState.COMPLETED, BlockchainState.VALIDATED) + + @asyncio.coroutine + def exec_test(): + ms = yield from identity.membership(self.community) + self.assertEqual(ms["blockNumber"], 0) + self.assertEqual(ms["blockHash"], "DA39A3EE5E6B4B0D3255BFEF95601890AFD80709") + self.assertEqual(ms["membership"], "IN") + self.assertEqual(ms["currency"], "test_currency") + + self.lp.run_until_complete(exec_test()) + mock.delete_mock() + + def test_identity_corrupted_membership(self): + mock = corrupted.get_mock() + time.sleep(2) + logging.debug(mock.pretend_url) + API.reverse_url = pretender_reversed(mock.pretend_url) + identity = Identity("john", "7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ", + LocalState.COMPLETED, BlockchainState.VALIDATED) + + @asyncio.coroutine + def exec_test(): + with self.assertRaises(MembershipNotFoundError): + yield from identity.membership(self.community) + + self.lp.run_until_complete(exec_test()) + mock.delete_mock() + + +if __name__ == '__main__': + logging.basicConfig(stream=sys.stderr) + logging.getLogger().setLevel(logging.DEBUG) + unittest.main() diff --git a/src/cutecoin/tests/core/wallet.py b/src/cutecoin/tests/core/test_wallet.py similarity index 100% rename from src/cutecoin/tests/core/wallet.py rename to src/cutecoin/tests/core/test_wallet.py diff --git a/src/cutecoin/tests/mocks/bma/corrupted.py b/src/cutecoin/tests/mocks/bma/corrupted.py new file mode 100644 index 0000000000000000000000000000000000000000..c27a7ce3d47c4b5e6889185656190be25098fe6c --- /dev/null +++ b/src/cutecoin/tests/mocks/bma/corrupted.py @@ -0,0 +1,22 @@ +import json +import time +from pretenders.client.http import HTTPMock +from pretenders.common.constants import FOREVER + +bma_memberships_empty_array = { + "pubkey": "7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ", + "uid": "john", + "sigDate": 123456789, + "memberships": [ ] +} + + +def get_mock(): + mock = HTTPMock('127.0.0.1', 50000) + + mock.when('GET /blockchain/memberships/7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ')\ + .reply(body=bytes(json.dumps(bma_memberships_empty_array), "utf-8"), + times=FOREVER, + headers={'Content-Type': 'application/json'}) + + return mock diff --git a/src/cutecoin/tests/mocks/bma/nice_blockchain.py b/src/cutecoin/tests/mocks/bma/nice_blockchain.py index 606fc66066635f13c9cad16d2ec5c22c47688e7d..76d21e57a626b90a6e7e2fbf74e4b8fc1a916af6 100644 --- a/src/cutecoin/tests/mocks/bma/nice_blockchain.py +++ b/src/cutecoin/tests/mocks/bma/nice_blockchain.py @@ -1,4 +1,5 @@ import json +import time from pretenders.client.http import HTTPMock from pretenders.common.constants import FOREVER @@ -35,6 +36,23 @@ bma_lookup_john = { ] } +bma_membership_john = { + "pubkey": "7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ", + "uid": "inso", + "sigDate": 1441130831, + "memberships": + [ + { + + "version": "1", + "currency": "test_currency", + "membership": "IN", + "blockNumber": 0, + "blockHash": "DA39A3EE5E6B4B0D3255BFEF95601890AFD80709" + } + ] +} + bma_lookup_doe = { "partial": False, "results": [ @@ -346,6 +364,12 @@ def get_mock(): times=FOREVER, headers={'Content-Type': 'application/json'}) + mock.when('GET /blockchain/memberships/7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ')\ + .reply(body=bytes(json.dumps(bma_membership_john), "utf-8"), + status=200, + times=FOREVER, + headers={'Content-Type': 'application/json'}) + mock.when('GET /wot/certifiers-of/FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn')\ .reply(body=b"No member matching this pubkey or uid", status=404,