From a77f2426b67301a55be9dde4ce7320bb4585eb79 Mon Sep 17 00:00:00 2001
From: inso <insomniak.fr@gmail.com>
Date: Mon, 9 Jan 2017 18:51:03 +0100
Subject: [PATCH] Fix tests and app startup

---
 src/sakia/data/files/user_parameters.py       |   2 +
 src/sakia/data/repositories/connections.py    |   2 +-
 .../gui/dialogs/connection_cfg/controller.py  |   1 +
 src/sakia/options.py                          |  19 +-
 .../functional/test_certification_dialog.py   |   2 +-
 .../functional/test_connection_cfg_dialog.py  |   8 +-
 .../tests/technical/test_documents_service.py |  39 ---
 .../tests/technical/test_network_service.py   |  52 ---
 src/sakia/tests/unit/core/__init__.py         |   1 -
 src/sakia/tests/unit/core/test_application.py |  28 --
 src/sakia/tests/unit/core/test_wallet.py      | 262 ---------------
 .../tests/unit/core/txhistory/__init__.py     |   1 -
 .../core/txhistory/test_txhistory_loading.py  |  62 ----
 .../tests/unit/data/test_node_connector.py    |   3 +-
 src/sakia/tests/unit/gui/test_context_menu.py | 164 ----------
 src/sakia/tests/unit/gui/test_generic_tree.py | 104 +++---
 src/sakia/tests/unit/gui/test_main_window.py  |  91 ------
 .../tests/unit/{core => }/money/__init__.py   |   0
 .../{core => }/money/test_quantitative.py     |   0
 .../money/test_quantitative_zsum.py           |   0
 .../unit/{core => }/money/test_relative.py    |   0
 .../{core => }/money/test_relative_zsum.py    |   0
 src/sakia/tests/unit/test_decorators.py       | 303 +++++++++---------
 23 files changed, 216 insertions(+), 928 deletions(-)
 delete mode 100644 src/sakia/tests/technical/test_documents_service.py
 delete mode 100644 src/sakia/tests/technical/test_network_service.py
 delete mode 100644 src/sakia/tests/unit/core/__init__.py
 delete mode 100644 src/sakia/tests/unit/core/test_application.py
 delete mode 100644 src/sakia/tests/unit/core/test_wallet.py
 delete mode 100644 src/sakia/tests/unit/core/txhistory/__init__.py
 delete mode 100644 src/sakia/tests/unit/core/txhistory/test_txhistory_loading.py
 delete mode 100644 src/sakia/tests/unit/gui/test_context_menu.py
 delete mode 100644 src/sakia/tests/unit/gui/test_main_window.py
 rename src/sakia/tests/unit/{core => }/money/__init__.py (100%)
 rename src/sakia/tests/unit/{core => }/money/test_quantitative.py (100%)
 rename src/sakia/tests/unit/{core => }/money/test_quantitative_zsum.py (100%)
 rename src/sakia/tests/unit/{core => }/money/test_relative.py (100%)
 rename src/sakia/tests/unit/{core => }/money/test_relative_zsum.py (100%)

diff --git a/src/sakia/data/files/user_parameters.py b/src/sakia/data/files/user_parameters.py
index 3c2db3f6..f0011606 100644
--- a/src/sakia/data/files/user_parameters.py
+++ b/src/sakia/data/files/user_parameters.py
@@ -16,6 +16,8 @@ class UserParametersFile:
 
     @classmethod
     def in_config_path(cls, config_path, profile_name):
+        if not os.path.exists(os.path.join(config_path, profile_name)):
+            os.makedirs(os.path.join(config_path, profile_name))
         return cls(os.path.join(config_path, profile_name, UserParametersFile.filename))
 
     def save(self, user_parameters):
diff --git a/src/sakia/data/repositories/connections.py b/src/sakia/data/repositories/connections.py
index 1783e0cf..fe4730b1 100644
--- a/src/sakia/data/repositories/connections.py
+++ b/src/sakia/data/repositories/connections.py
@@ -114,7 +114,7 @@ class ConnectionsRepo:
         Drop an existing connection from the database
         :param sakia.data.entities.Connection connection: the connection to update
         """
-        where_fields = attr.astuple(connection, filter=attr.filters.include(*ConnectionsRepo._primary_connections))
+        where_fields = attr.astuple(connection, filter=attr.filters.include(*ConnectionsRepo._primary_keys))
         self._conn.execute("""DELETE FROM connections
                               WHERE
                               currency=? AND
diff --git a/src/sakia/gui/dialogs/connection_cfg/controller.py b/src/sakia/gui/dialogs/connection_cfg/controller.py
index e3bce3b9..74af0601 100644
--- a/src/sakia/gui/dialogs/connection_cfg/controller.py
+++ b/src/sakia/gui/dialogs/connection_cfg/controller.py
@@ -188,6 +188,7 @@ class ConnectionConfigController(QObject):
             if self.model.node_connector:
                 await self.model.node_connector.session.close()
         except (NoPeerAvailable, DuniterError) as e:
+            raise
             self._logger.debug(str(e))
             self.view.stacked_pages.setCurrentWidget(self.view.page_connection)
             self.step_node = asyncio.Future()
diff --git a/src/sakia/options.py b/src/sakia/options.py
index 1952c0f1..5957f1c0 100644
--- a/src/sakia/options.py
+++ b/src/sakia/options.py
@@ -12,7 +12,7 @@ from optparse import OptionParser
 from os import environ, path, makedirs
 
 
-def config_path():
+def config_path_factory():
     if "XDG_CONFIG_HOME" in environ:
         env_path = environ["XDG_CONFIG_HOME"]
     elif "HOME" in environ:
@@ -26,7 +26,7 @@ def config_path():
 
 @attr.s()
 class SakiaOptions:
-    config_path = attr.ib(default=attr.Factory(config_path))
+    config_path = attr.ib(default=attr.Factory(config_path_factory))
     _logger = attr.ib(default=attr.Factory(lambda: logging.getLogger('sakia')))
 
     @classmethod
@@ -59,10 +59,11 @@ class SakiaOptions:
             self._logger.setLevel(logging.INFO)
             formatter = logging.Formatter('%(levelname)s:%(message)s')
 
-        logging.getLogger('quamash').setLevel(logging.INFO)
-        file_handler = RotatingFileHandler(path.join(self.config_path, 'sakia.log'), 'a', 1000000, 10)
-        file_handler.setFormatter(formatter)
-        stream_handler = StreamHandler()
-        stream_handler.setFormatter(formatter)
-        self._logger.handlers = [file_handler, stream_handler]
-        self._logger.propagate = False
+        if options.debug or options.verbose:
+            logging.getLogger('quamash').setLevel(logging.INFO)
+            file_handler = RotatingFileHandler(path.join(self.config_path, 'sakia.log'), 'a', 1000000, 10)
+            file_handler.setFormatter(formatter)
+            stream_handler = StreamHandler()
+            stream_handler.setFormatter(formatter)
+            self._logger.handlers = [file_handler, stream_handler]
+            self._logger.propagate = False
diff --git a/src/sakia/tests/functional/test_certification_dialog.py b/src/sakia/tests/functional/test_certification_dialog.py
index c96668e9..db32d6d3 100644
--- a/src/sakia/tests/functional/test_certification_dialog.py
+++ b/src/sakia/tests/functional/test_certification_dialog.py
@@ -30,7 +30,7 @@ async def test_certification_init_community(application_with_one_connection, fak
         certification_dialog.search_user.view.search()
         await asyncio.sleep(0.1)
         certification_dialog.search_user.view.node_selected.emit(0)
-        await asyncio.sleep(0.1)
+        await asyncio.sleep(1)
         assert certification_dialog.user_information.model.identity.uid == "alice"
         assert certification_dialog.view.button_box.button(QDialogButtonBox.Ok).isEnabled()
         QTest.mouseClick(certification_dialog.view.button_box.button(QDialogButtonBox.Ok), Qt.LeftButton)
diff --git a/src/sakia/tests/functional/test_connection_cfg_dialog.py b/src/sakia/tests/functional/test_connection_cfg_dialog.py
index d7f42124..fb831912 100644
--- a/src/sakia/tests/functional/test_connection_cfg_dialog.py
+++ b/src/sakia/tests/functional/test_connection_cfg_dialog.py
@@ -38,9 +38,9 @@ async def test_register_empty_blockchain(application, fake_server, bob):
         QTest.keyClicks(connection_config_dialog.view.edit_server, fake_server.peer_doc().endpoints[0].ipv4)
         connection_config_dialog.view.spinbox_port.setValue(fake_server.peer_doc().endpoints[0].port)
         assert connection_config_dialog.view.stacked_pages.currentWidget() == connection_config_dialog.view.page_node
-        await asyncio.sleep(0.1)
+        await asyncio.sleep(0.6)
         QTest.mouseClick(connection_config_dialog.view.button_register, Qt.LeftButton)
-        await asyncio.sleep(0.1)
+        await asyncio.sleep(0.6)
 
         assert connection_config_dialog.view.stacked_pages.currentWidget() == connection_config_dialog.view.page_connection
         assert_key_parameters_behaviour(connection_config_dialog, bob)
@@ -48,7 +48,7 @@ async def test_register_empty_blockchain(application, fake_server, bob):
         connection_config_dialog.model.connection.password = bob.password
         await asyncio.sleep(1)
         assert connection_config_dialog.view.stacked_pages.currentWidget() == connection_config_dialog.view.page_services
-        assert len(ConnectionsProcessor.instanciate(application).connections(fake_server.currency)) == 1
+        assert len(ConnectionsProcessor.instanciate(application).connections(fake_server.forge.currency)) == 1
 
     application.loop.call_later(10, close_dialog)
     asyncio.ensure_future(exec_test())
@@ -77,7 +77,7 @@ async def test_connect(application, simple_fake_server, bob):
         await asyncio.sleep(1)
 
         assert connection_config_dialog.view.stacked_pages.currentWidget() == connection_config_dialog.view.page_services
-        assert len(ConnectionsProcessor.instanciate(application).connections(simple_fake_server.currency)) == 1
+        assert len(ConnectionsProcessor.instanciate(application).connections(simple_fake_server.forge.currency)) == 1
 
     application.loop.call_later(10, close_dialog)
     asyncio.ensure_future(exec_test())
diff --git a/src/sakia/tests/technical/test_documents_service.py b/src/sakia/tests/technical/test_documents_service.py
deleted file mode 100644
index 076c4bc4..00000000
--- a/src/sakia/tests/technical/test_documents_service.py
+++ /dev/null
@@ -1,39 +0,0 @@
-import asyncio
-import unittest
-import sqlite3
-import aiohttp
-from duniterpy.documents import BlockUID, Peer
-from sakia.tests import QuamashTest
-from sakia.services import DocumentsService
-from sakia.data.connectors import NodeConnector, BmaConnector
-from sakia.data.repositories import NodesRepo, SakiaDatabase, BlockchainsRepo, IdentitiesRepo
-from sakia.data.processors import NodesProcessor, BlockchainProcessor, IdentitiesProcessor
-
-
-class TestDocumentsService(unittest.TestCase, QuamashTest):
-    def setUp(self):
-        self.setUpQuamash()
-        sqlite3.register_adapter(BlockUID, str)
-        sqlite3.register_adapter(bool, int)
-        sqlite3.register_adapter(list, lambda ls: '\n'.join([str(v) for v in ls]))
-        sqlite3.register_adapter(tuple, lambda ls: '\n'.join([str(v) for v in ls]))
-        sqlite3.register_converter("BOOLEAN", lambda v: bool(int(v)))
-        self.con = sqlite3.connect(":memory:", detect_types=sqlite3.PARSE_DECLTYPES)
-
-    def tearDown(self):
-        self.tearDownQuamash()
-
-    def test_certify(self):
-        meta_repo = SakiaDatabase(self.con)
-        meta_repo.prepare()
-        meta_repo.upgrade_database()
-        nodes_repo = NodesRepo(self.con)
-        nodes_processor = NodesProcessor(nodes_repo)
-        bma_connector = BmaConnector(nodes_processor)
-        blockchain_repo = BlockchainsRepo(self.con)
-        identities_repo = IdentitiesRepo(self.con)
-        blockchain_processor = BlockchainProcessor(blockchain_repo, bma_connector)
-        identities_processor = IdentitiesProcessor(identities_repo, bma_connector)
-        documents_service = DocumentsService(bma_connector, blockchain_processor, identities_processor)
-        #TODO: Build a framework to test documents broadcasting
-
diff --git a/src/sakia/tests/technical/test_network_service.py b/src/sakia/tests/technical/test_network_service.py
deleted file mode 100644
index 49f5477c..00000000
--- a/src/sakia/tests/technical/test_network_service.py
+++ /dev/null
@@ -1,52 +0,0 @@
-import asyncio
-import unittest
-import sqlite3
-import aiohttp
-from duniterpy.documents import BlockUID, Peer
-from sakia.tests import QuamashTest
-from sakia.services import NetworkService
-from sakia.data.connectors import NodeConnector
-from sakia.data.repositories import NodesRepo, SakiaDatabase
-from sakia.data.processors import NodesProcessor
-
-
-class TestNetworkService(unittest.TestCase, QuamashTest):
-    def setUp(self):
-        self.setUpQuamash()
-        sqlite3.register_adapter(BlockUID, str)
-        sqlite3.register_adapter(bool, int)
-        sqlite3.register_adapter(list, lambda ls: '\n'.join([str(v) for v in ls]))
-        sqlite3.register_adapter(tuple, lambda ls: '\n'.join([str(v) for v in ls]))
-        sqlite3.register_converter("BOOLEAN", lambda v: bool(int(v)))
-        self.con = sqlite3.connect(":memory:", detect_types=sqlite3.PARSE_DECLTYPES)
-
-    def tearDown(self):
-        self.tearDownQuamash()
-
-    def test_network_discovering(self):
-        meta_repo = SakiaDatabase(self.con)
-        meta_repo.prepare()
-        meta_repo.upgrade_database()
-        nodes_repo = NodesRepo(self.con)
-
-        async def exec_test():
-            with aiohttp.ClientSession() as session:
-                peering = await session.get("http://peer.duniter.org:8999/network/peering")
-                self.assertEqual(peering.status, 200)
-                data = await peering.json()
-                peer_document = Peer.from_signed_raw("{0}{1}\n".format(data["raw"], data["signature"]))
-                node_connector = NodeConnector.from_peer(peer_document.currency, peer_document, session)
-
-                processor = NodesProcessor(peer_document.currency, nodes_repo)
-                network_service = NetworkService.create(processor, node_connector)
-
-                network_service._must_crawl = True
-                asyncio.ensure_future(network_service.discovery_loop())
-                network_service.refresh_once()
-                await asyncio.sleep(20)
-                self.assertGreater(len(processor.nodes()), 1)
-                await asyncio.sleep(20)
-                self.assertGreater(len(processor.nodes()), 2)
-                await network_service.stop_coroutines(True)
-
-        self.lp.run_until_complete(exec_test())
diff --git a/src/sakia/tests/unit/core/__init__.py b/src/sakia/tests/unit/core/__init__.py
deleted file mode 100644
index 39ab2a0b..00000000
--- a/src/sakia/tests/unit/core/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-__author__ = 'inso'
diff --git a/src/sakia/tests/unit/core/test_application.py b/src/sakia/tests/unit/core/test_application.py
deleted file mode 100644
index 3065f89c..00000000
--- a/src/sakia/tests/unit/core/test_application.py
+++ /dev/null
@@ -1,28 +0,0 @@
-import aiohttp
-import sys
-import unittest
-import asyncio
-from asynctest.mock import Mock, CoroutineMock, patch
-from PyQt5.QtCore import QLocale
-from sakia.tests import QuamashTest
-from sakia.core import Application
-
-
-class TestApplication(unittest.TestCase, QuamashTest):
-    def setUp(self):
-        self.setUpQuamash()
-        QLocale.setDefault(QLocale("en_GB"))
-
-    def tearDown(self):
-        self.tearDownQuamash()
-
-    @patch('sakia.core.registry.IdentitiesRegistry')
-    @patch('aiohttp.get', CoroutineMock(side_effect=lambda *args, **kwargs: exec('raise aiohttp.errors.TimeoutError()')))
-    def test_get_last_version_timeout(self, identities_registry):
-        app = Application(self.qapplication, self.lp, identities_registry)
-
-        async def exec_test():
-            app.get_last_version()
-            asyncio.sleep(5)
-
-        self.lp.run_until_complete(exec_test())
diff --git a/src/sakia/tests/unit/core/test_wallet.py b/src/sakia/tests/unit/core/test_wallet.py
deleted file mode 100644
index eee64a6e..00000000
--- a/src/sakia/tests/unit/core/test_wallet.py
+++ /dev/null
@@ -1,262 +0,0 @@
-import unittest
-import pypeg2
-from unittest.mock import MagicMock, PropertyMock
-from asynctest import CoroutineMock
-from duniterpy.grammars import output
-from duniterpy.documents import BlockUID
-from PyQt5.QtCore import QLocale
-from sakia.core.registry.identities import IdentitiesRegistry
-from sakia.core import Wallet
-from sakia.tests import QuamashTest
-
-
-class TestWallet(unittest.TestCase, QuamashTest):
-    def setUp(self):
-        self.setUpQuamash()
-        QLocale.setDefault(QLocale("en_GB"))
-        self.identities_registry = IdentitiesRegistry({})
-
-    def tearDown(self):
-        self.tearDownQuamash()
-
-    def test_load_save_wallet(self):
-        wallet = Wallet(0, "7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ",
-                             "Wallet 1", self.identities_registry)
-
-        json_data = wallet.jsonify()
-        wallet_from_json = Wallet.load(json_data, self.identities_registry)
-        self.assertEqual(wallet.walletid, wallet_from_json.walletid)
-        self.assertEqual(wallet.pubkey, wallet_from_json.pubkey)
-        self.assertEqual(wallet.name, wallet_from_json.name)
-        self.assertEqual(wallet._identities_registry, wallet_from_json._identities_registry)
-
-    def test_prepare_tx_base_0(self):
-        community = MagicMock("sakia.core.Community")
-        community.currency = "test_currency"
-        cache = MagicMock("sakia.core.txhistory.TxHistory")
-        cache.available_sources = [{
-            "pubkey": "7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ",
-            "type": "D",
-            "noffset": 2,
-            "identifier": "FCAD5A388AC8A811B45A9334A375585E77071AA9F6E5B6896582961A6C66F365",
-            "amount": 15,
-            "base": 0
-        },
-            {
-                "pubkey": "7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ",
-                "type": "D",
-                "noffset": 4,
-                "identifier": "A0AC57E2E4B24D66F2D25E66D8501D8E881D9E6453D1789ED753D7D426537ED5",
-                "amount": 85,
-                "base": 0
-            },
-            {
-                "pubkey": "FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn",
-                "type": "T",
-                "noffset": 4,
-                "identifier": "7518C700E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67",
-                "amount": 11,
-                "base": 1
-            }]
-        wallet = Wallet(0, "7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ",
-                        "Wallet 1", self.identities_registry)
-        wallet.caches["test_currency"] = cache
-        tx = wallet.prepare_tx("FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn",
-                               BlockUID(32, "000005E0F228038E4DDD4F6CA4ACB01EC88FBAF8"),
-                               99, "", community)
-        self.assertEqual(tx.blockstamp.number, 32)
-        self.assertEqual(tx.blockstamp.sha_hash, "000005E0F228038E4DDD4F6CA4ACB01EC88FBAF8")
-        self.assertEqual(len(tx.issuers), 1)
-        self.assertEqual(tx.issuers[0], "7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ")
-        self.assertEqual(len(tx.inputs), 2)
-        self.assertEqual(tx.inputs[0].origin_id, "FCAD5A388AC8A811B45A9334A375585E77071AA9F6E5B6896582961A6C66F365")
-        self.assertEqual(tx.inputs[0].source, "D")
-        self.assertEqual(tx.inputs[0].index, 2)
-        self.assertEqual(tx.inputs[1].origin_id, "A0AC57E2E4B24D66F2D25E66D8501D8E881D9E6453D1789ED753D7D426537ED5")
-        self.assertEqual(tx.inputs[1].source, "D")
-        self.assertEqual(tx.inputs[1].index, 4)
-        self.assertEqual(len(tx.outputs), 2)
-        self.assertEqual(tx.outputs[0].amount, 99)
-        self.assertEqual(tx.outputs[0].base, 0)
-        self.assertEqual(pypeg2.compose(tx.outputs[0].conditions, output.Condition),
-                         "SIG(FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn)")
-        self.assertEqual(tx.outputs[1].amount, 1)
-        self.assertEqual(tx.outputs[1].base, 0)
-        self.assertEqual(pypeg2.compose(tx.outputs[1].conditions, output.Condition),
-                         "SIG(7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ)")
-        self.assertEqual(len(tx.unlocks), 2)
-        self.assertEqual(tx.unlocks[0].index, 0)
-        self.assertEqual(tx.unlocks[0].parameters[0].index, 0)
-        self.assertEqual(tx.unlocks[1].index, 1)
-        self.assertEqual(tx.unlocks[0].parameters[0].index, 0)
-        self.assertEqual(tx.raw(), """Version: 2
-Type: Transaction
-Currency: test_currency
-Locktime: 0
-Issuers:
-7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ
-Inputs:
-D:FCAD5A388AC8A811B45A9334A375585E77071AA9F6E5B6896582961A6C66F365:2
-D:A0AC57E2E4B24D66F2D25E66D8501D8E881D9E6453D1789ED753D7D426537ED5:4
-Unlocks:
-0:SIG(0)
-1:SIG(0)
-Outputs:
-99:0:SIG(FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn)
-1:0:SIG(7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ)
-Comment:""" + " \n")
-
-    def test_prepare_tx_base_1(self):
-        community = MagicMock("sakia.core.Community")
-        community.currency = "test_currency"
-        cache = MagicMock("sakia.core.txhistory.TxHistory")
-        cache.available_sources = [{
-                "pubkey": "7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ",
-                "type": "D",
-                "noffset": 2,
-                "identifier": "FCAD5A388AC8A811B45A9334A375585E77071AA9F6E5B6896582961A6C66F365",
-                "amount": 15,
-                "base": 0
-            },
-            {
-                "pubkey": "7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ",
-                "type": "D",
-                "noffset": 4,
-                "identifier": "A0AC57E2E4B24D66F2D25E66D8501D8E881D9E6453D1789ED753D7D426537ED5",
-                "amount": 85,
-                "base": 0
-            },
-            {
-                "pubkey": "FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn",
-                "type": "T",
-                "noffset": 4,
-                "identifier": "7518C700E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67",
-                "amount": 11,
-                "base": 1
-            }]
-        wallet = Wallet(0, "7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ",
-                             "Wallet 1", self.identities_registry)
-        wallet.caches["test_currency"] = cache
-        tx = wallet.prepare_tx("FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn",
-                               BlockUID(32, "000005E0F228038E4DDD4F6CA4ACB01EC88FBAF8"),
-                               100, "", community)
-        self.assertEqual(tx.blockstamp.number, 32)
-        self.assertEqual(tx.blockstamp.sha_hash, "000005E0F228038E4DDD4F6CA4ACB01EC88FBAF8")
-        self.assertEqual(len(tx.issuers), 1)
-        self.assertEqual(tx.issuers[0], "7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ")
-        self.assertEqual(len(tx.inputs), 1)
-        self.assertEqual(tx.inputs[0].origin_id, "7518C700E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67")
-        self.assertEqual(tx.inputs[0].source, "T")
-        self.assertEqual(tx.inputs[0].index, 4)
-        self.assertEqual(len(tx.outputs), 2)
-        self.assertEqual(tx.outputs[0].amount, 10)
-        self.assertEqual(tx.outputs[0].base, 1)
-        self.assertEqual(pypeg2.compose(tx.outputs[0].conditions, output.Condition),
-                         "SIG(FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn)")
-        self.assertEqual(tx.outputs[1].amount, 1)
-        self.assertEqual(tx.outputs[1].base, 1)
-        self.assertEqual(pypeg2.compose(tx.outputs[1].conditions, output.Condition),
-                         "SIG(7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ)")
-        self.assertEqual(len(tx.unlocks), 1)
-        self.assertEqual(tx.unlocks[0].index, 0)
-        self.assertEqual(tx.unlocks[0].parameters[0].index, 0)
-        self.assertEqual(tx.raw(), """Version: 2
-Type: Transaction
-Currency: test_currency
-Locktime: 0
-Issuers:
-7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ
-Inputs:
-T:7518C700E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67:4
-Unlocks:
-0:SIG(0)
-Outputs:
-10:1:SIG(FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn)
-1:1:SIG(7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ)
-Comment:""" + " \n")
-
-    def test_prepare_tx_base_1_overheads(self):
-        community = MagicMock("sakia.core.Community")
-        community.currency = "test_currency"
-        cache = MagicMock("sakia.core.txhistory.TxHistory")
-        cache.available_sources = [{
-                "pubkey": "7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ",
-                "type": "D",
-                "noffset": 2,
-                "identifier": "FCAD5A388AC8A811B45A9334A375585E77071AA9F6E5B6896582961A6C66F365",
-                "amount": 15,
-                "base": 0
-            },
-            {
-                "pubkey": "7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ",
-                "type": "D",
-                "noffset": 4,
-                "identifier": "A0AC57E2E4B24D66F2D25E66D8501D8E881D9E6453D1789ED753D7D426537ED5",
-                "amount": 85,
-                "base": 0
-            },
-            {
-                "pubkey": "FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn",
-                "type": "T",
-                "noffset": 4,
-                "identifier": "7518C700E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67",
-                "amount": 11,
-                "base": 1
-            }]
-        wallet = Wallet(0, "7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ",
-                             "Wallet 1", self.identities_registry)
-        wallet.caches["test_currency"] = cache
-        tx = wallet.prepare_tx("FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn",
-                               BlockUID(32, "000005E0F228038E4DDD4F6CA4ACB01EC88FBAF8"),
-                               101, "", community)
-        self.assertEqual(tx.blockstamp.number, 32)
-        self.assertEqual(tx.blockstamp.sha_hash, "000005E0F228038E4DDD4F6CA4ACB01EC88FBAF8")
-        self.assertEqual(len(tx.issuers), 1)
-        self.assertEqual(tx.issuers[0], "7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ")
-        self.assertEqual(len(tx.inputs), 2)
-        self.assertEqual(tx.inputs[0].origin_id, "7518C700E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67")
-        self.assertEqual(tx.inputs[0].source, "T")
-        self.assertEqual(tx.inputs[0].index, 4)
-        self.assertEqual(tx.inputs[1].origin_id, "FCAD5A388AC8A811B45A9334A375585E77071AA9F6E5B6896582961A6C66F365")
-        self.assertEqual(tx.inputs[1].source, "D")
-        self.assertEqual(tx.inputs[1].index, 2)
-        self.assertEqual(len(tx.outputs), 4)
-        self.assertEqual(tx.outputs[0].amount, 1)
-        self.assertEqual(tx.outputs[0].base, 0)
-        self.assertEqual(pypeg2.compose(tx.outputs[0].conditions, output.Condition),
-                         "SIG(FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn)")
-        self.assertEqual(tx.outputs[1].amount, 10)
-        self.assertEqual(tx.outputs[1].base, 1)
-        self.assertEqual(pypeg2.compose(tx.outputs[1].conditions, output.Condition),
-                         "SIG(FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn)")
-        self.assertEqual(tx.outputs[2].amount, 14)
-        self.assertEqual(tx.outputs[2].base, 0)
-        self.assertEqual(pypeg2.compose(tx.outputs[2].conditions, output.Condition),
-                         "SIG(7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ)")
-        self.assertEqual(tx.outputs[3].amount, 1)
-        self.assertEqual(tx.outputs[3].base, 1)
-        self.assertEqual(pypeg2.compose(tx.outputs[3].conditions, output.Condition),
-                         "SIG(7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ)")
-        self.assertEqual(len(tx.unlocks), 2)
-        self.assertEqual(tx.unlocks[0].index, 0)
-        self.assertEqual(tx.unlocks[0].parameters[0].index, 0)
-        self.assertEqual(tx.unlocks[1].index, 1)
-        self.assertEqual(tx.unlocks[1].parameters[0].index, 0)
-        self.assertEqual(tx.raw(), """Version: 2
-Type: Transaction
-Currency: test_currency
-Locktime: 0
-Issuers:
-7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ
-Inputs:
-T:7518C700E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67:4
-D:FCAD5A388AC8A811B45A9334A375585E77071AA9F6E5B6896582961A6C66F365:2
-Unlocks:
-0:SIG(0)
-1:SIG(0)
-Outputs:
-1:0:SIG(FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn)
-10:1:SIG(FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn)
-14:0:SIG(7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ)
-1:1:SIG(7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ)
-Comment:""" + " \n")
diff --git a/src/sakia/tests/unit/core/txhistory/__init__.py b/src/sakia/tests/unit/core/txhistory/__init__.py
deleted file mode 100644
index 39ab2a0b..00000000
--- a/src/sakia/tests/unit/core/txhistory/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-__author__ = 'inso'
diff --git a/src/sakia/tests/unit/core/txhistory/test_txhistory_loading.py b/src/sakia/tests/unit/core/txhistory/test_txhistory_loading.py
deleted file mode 100644
index e6d69cf1..00000000
--- a/src/sakia/tests/unit/core/txhistory/test_txhistory_loading.py
+++ /dev/null
@@ -1,62 +0,0 @@
-import logging
-import time
-import unittest
-
-from PyQt5.QtCore import QLocale
-from sakia.core.net import Network, Node
-from sakia.core.net.api.bma.access import BmaAccess
-
-from duniterpy.documents.peer import BMAEndpoint
-from sakia.app import Application
-from sakia.core import Account, Community, Wallet
-from sakia.core.registry.identities import IdentitiesRegistry
-from sakia.tests import QuamashTest
-from sakia.tests.mocks.bma import nice_blockchain
-
-
-class TestTxHistory(unittest.TestCase, QuamashTest):
-    def setUp(self):
-        self.setUpQuamash()
-        QLocale.setDefault(QLocale("en_GB"))
-        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", "", 50005)
-        self.node = Node("test_currency", [self.endpoint],
-                         "", "HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk",
-                         nice_blockchain.bma_blockchain_current, Node.ONLINE,
-                         time.time(), {}, "duniter", "0.14.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)
-
-        self.wallet = Wallet(0, "7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ",
-                             "Wallet 1", self.identities_registry)
-        self.wallet.init_cache(self.application, self.community)
-
-        # Salt/password : "testsakia/testsakia"
-        # Pubkey : 7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ
-        self.account = Account("testsakia", "7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ",
-                               "john", [self.community], [self.wallet], [], self.identities_registry)
-
-    def tearDown(self):
-        self.tearDownQuamash()
-
-    # this test fails with old algorithm
-    def notest_txhistory_reload(self):
-        mock = nice_blockchain.get_mock()
-        time.sleep(2)
-        logging.debug(mock.pretend_url)
-
-        received_list = []
-        self.lp.run_until_complete(self.wallet.caches[self.community.currency].
-                                   refresh(self.community, received_list))
-        self.assertEquals(len(received_list), 2)
-        received_value = sum([r.metadata['amount'] for r in received_list])
-        self.assertEqual(received_value, 60)
-        self.assertEqual(len(self.wallet.dividends(self.community)), 2)
-        dividends_value = sum([ud['amount'] for ud in self.wallet.dividends(self.community)])
-        self.assertEqual(dividends_value, 15)
-        mock.delete_mock()
diff --git a/src/sakia/tests/unit/data/test_node_connector.py b/src/sakia/tests/unit/data/test_node_connector.py
index 01f48e1c..9b316e01 100644
--- a/src/sakia/tests/unit/data/test_node_connector.py
+++ b/src/sakia/tests/unit/data/test_node_connector.py
@@ -1,4 +1,3 @@
-from unittest.mock import Mock
 from duniterpy.documents import Peer
 from sakia.data.connectors import NodeConnector
 
@@ -13,7 +12,7 @@ Endpoints:
 BASIC_MERKLED_API duniter.inso.ovh 80
 82o1sNCh1bLpUXU6nacbK48HBcA9Eu2sPkL1/3c2GtDPxBUZd2U2sb7DxwJ54n6ce9G0Oy7nd1hCxN3fS0oADw==
 """)
-    connector = NodeConnector.from_peer('meta_brouzouf', peer, Mock("aiohttp.ClientSession"))
+    connector = NodeConnector.from_peer('meta_brouzouf', peer)
     assert connector.node.pubkey == "8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU"
     assert connector.node.endpoints[0].inline() == "BASIC_MERKLED_API duniter.inso.ovh 80"
     assert connector.node.currency == "meta_brouzouf"
diff --git a/src/sakia/tests/unit/gui/test_context_menu.py b/src/sakia/tests/unit/gui/test_context_menu.py
deleted file mode 100644
index c1ee0b9f..00000000
--- a/src/sakia/tests/unit/gui/test_context_menu.py
+++ /dev/null
@@ -1,164 +0,0 @@
-import unittest
-from unittest.mock import patch, MagicMock, Mock
-from asynctest.mock import CoroutineMock
-from PyQt5.QtCore import QLocale
-from sakia.tests import QuamashTest
-from sakia.tests.mocks.bma import nice_blockchain
-from sakia.gui.widgets.context_menu import ContextMenu
-from duniterpy.documents import Membership, BlockUID
-from sakia.tools.exceptions import MembershipNotFoundError
-
-
-class TestContextMenu(unittest.TestCase, QuamashTest):
-    def setUp(self):
-        self.setUpQuamash()
-        QLocale.setDefault(QLocale("en_GB"))
-
-        self.identity = Mock(specs='sakia.core.registry.Identity')
-        self.identity.pubkey = "HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk"
-        self.identity.uid = "A"
-
-        self.app = MagicMock(specs='sakia.core.Application')
-        self.account = MagicMock(specs='sakia.core.Account')
-        self.community = MagicMock(specs='sakia.core.Community')
-        self.password_asker = MagicMock(specs='sakia.gui.password_asker.PasswordAsker')
-
-    def tearDown(self):
-        self.tearDownQuamash()
-
-    @patch('PyQt5.QtWidgets.QMenu', create=True)
-    def test_view_in_wot(self, qmenu):
-        wot_refreshed = False
-
-        def refresh_wot(identity):
-            nonlocal wot_refreshed
-            self.assertEqual(identity, self.identity)
-            wot_refreshed = True
-
-        async def exec_test():
-            context_menu = ContextMenu(qmenu, self.app, self.account, self.community, self.password_asker)
-            context_menu.view_identity_in_wot.connect(refresh_wot)
-            context_menu.view_wot(self.identity)
-
-        self.lp.run_until_complete(exec_test())
-        self.assertTrue(wot_refreshed)
-
-    @patch('PyQt5.QtWidgets.QMenu', create=True)
-    def test_copy_pubkey_to_clipboard(self, qmenu):
-        app = Mock('sakia.core.Application')
-
-        async def exec_test():
-            context_menu = ContextMenu(qmenu, self.app, self.account, self.community, self.password_asker)
-            context_menu.copy_pubkey_to_clipboard(self.identity)
-
-        self.lp.run_until_complete(exec_test())
-        self.assertEqual(self.qapplication.clipboard().text(), "HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk")
-
-    @patch('PyQt5.QtWidgets.QMenu', create=True)
-    def test_copy_block_to_clipboard(self, qmenu):
-        self.community.get_block = CoroutineMock(side_effect=lambda n: nice_blockchain.bma_blockchain_current if n == 15 \
-            else nice_blockchain.bma_blockchain_0)
-        self.qapplication.clipboard().clear()
-
-        async def exec_test():
-            context_menu = ContextMenu(qmenu, self.app, self.account, self.community, self.password_asker)
-            context_menu.community = self.community
-            context_menu.copy_block_to_clipboard(15)
-
-        self.lp.run_until_complete(exec_test())
-        raw_block = "{0}{1}\n".format(nice_blockchain.bma_blockchain_current["raw"],
-                                      nice_blockchain.bma_blockchain_current["signature"])
-        self.assertEqual(self.qapplication.clipboard().text(), raw_block)
-
-    @patch('PyQt5.QtWidgets.QMenu', create=True)
-    def test_copy_membership_to_clipboard(self, qmenu):
-        ms_data = {
-                    "version": 2,
-                    "currency": "meta_brouzouf",
-                    "membership": "IN",
-                    "blockNumber": 49116,
-                    "blockHash": "7518C700E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67",
-                    "written": 49119
-                }
-        ms_document = Membership(ms_data["version"], ms_data["currency"], self.identity.pubkey,
-                                 BlockUID(ms_data["blockNumber"], ms_data["blockHash"]),
-                                 ms_data["membership"], self.identity.uid, "49116-7518C700E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67",
-                                 "znWiWP7Sy9gg9pZq4YWKNpel8MM16VBM1lgBg2gWjSonnc+KVRCtQng5JB4JD0PgJJ0F8jdITuggFrRwqRfzAA==")
-        self.identity.membership = CoroutineMock(return_value=ms_data)
-        self.community.get_block = CoroutineMock(return_value={
-  "version": 2,
-  "nonce": 127424,
-  "number": 49119,
-  "powMin": 5,
-  "time": 1453921638,
-  "medianTime": 1453912797,
-  "membersCount": 18,
-  "monetaryMass": 14028534972234185000,
-  "currency": "meta_brouzouf",
-  "issuer": "HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk",
-  "signature": "ZmjhoRubftJ/T2WYQ3gaDeTGGUJ3beUshtlWn1k/r5opk0vt48KG3w+9JU0T9YFR5uezllaek9efoNwAHRBLDw==",
-  "hash": "49E2A1D1131F1496FAD6EDAE794A9ADBFA8844029675E3732D3B027ABB780243",
-  "innerhash": "273DE1845F8A63677D69DD427E00DAD73D9AEDBA80356A2E0D2152939D9DAF0C",
-  "parameters": "",
-  "previousHash": "000005C27A1636FE07AB01766FBA060565142D79",
-  "previousIssuer": "HBSSmqZjT4UQKsCntTSmZbu7iRP14HYtifLE6mW1PsBD",
-  "dividend": None,
-  "identities": [],
-  "joiners": [
-    "HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk:znWiWP7Sy9gg9pZq4YWKNpel8MM16VBM1lgBg2gWjSonnc+KVRCtQng5JB4JD0PgJJ0F8jdITuggFrRwqRfzAA==:49116-7518C700E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67:49116-7518C700E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67:A"
-  ],
-  "actives": [],
-  "leavers": [],
-  "excluded": [],
-  "revoked": [],
-  "certifications": [],
-  "transactions": [],
-  "raw": """Version: 2
-Type: Block
-Currency: meta_brouzouf
-Number: 49119
-PoWMin: 5
-Time: 1453921638
-MedianTime: 1453912797
-Issuer: HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk
-PreviousHash: 000005C27A1636FE07AB01766FBA060565142D79
-PreviousIssuer: HBSSmqZjT4UQKsCntTSmZbu7iRP14HYtifLE6mW1PsBD
-MembersCount: 18
-Identities:
-Joiners:
-HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk:znWiWP7Sy9gg9pZq4YWKNpel8MM16VBM1lgBg2gWjSonnc+KVRCtQng5JB4JD0PgJJ0F8jdITuggFrRwqRfzAA==:49116-7518C700E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67:49116-7518C700E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67:A
-Actives:
-Leavers:
-Revoked:
-Excluded:
-Certifications:
-Transactions:
-InnerHash: 273DE1845F8A63677D69DD427E00DAD73D9AEDBA80356A2E0D2152939D9DAF0C
-Nonce: 127424
-"""
-})
-        self.qapplication.clipboard().clear()
-
-        async def exec_test():
-            context_menu = ContextMenu(qmenu, self.app, self.account, self.community, self.password_asker)
-            context_menu.community = self.community
-            context_menu.copy_membership_to_clipboard(self.identity)
-
-        self.lp.run_until_complete(exec_test())
-        self.assertEqual(self.qapplication.clipboard().text(), ms_document.signed_raw())
-
-    @patch('PyQt5.QtWidgets.QMenu', create=True)
-    def test_copy_membership_to_clipboard_not_found(self, qmenu):
-        def raiser():
-            raise MembershipNotFoundError("inso", "meta_brouzouf")
-        self.identity.membership = CoroutineMock(side_effect=lambda c: raiser())
-
-        self.qapplication.clipboard().clear()
-
-        async def exec_test():
-            context_menu = ContextMenu(qmenu, self.app, self.account, self.community, self.password_asker)
-            context_menu.community = self.community
-            context_menu.copy_membership_to_clipboard(self.identity)
-
-        self.lp.run_until_complete(exec_test())
-        self.assertEqual(self.qapplication.clipboard().text(), "")
diff --git a/src/sakia/tests/unit/gui/test_generic_tree.py b/src/sakia/tests/unit/gui/test_generic_tree.py
index d26ebc96..8a462f5f 100644
--- a/src/sakia/tests/unit/gui/test_generic_tree.py
+++ b/src/sakia/tests/unit/gui/test_generic_tree.py
@@ -1,66 +1,56 @@
-import unittest
-from unittest.mock import patch, MagicMock, Mock, PropertyMock
-from PyQt5.QtCore import QLocale
-from sakia.tests import QuamashTest
+from PyQt5.QtCore import QModelIndex
 from sakia.models.generic_tree import GenericTreeModel
-from PyQt5.QtWidgets import QTreeView, QDialog
 
 
-class TestGenericTree(unittest.TestCase, QuamashTest):
-    def setUp(self):
-        self.setUpQuamash()
-        QLocale.setDefault(QLocale("en_GB"))
-
-    def tearDown(self):
-        self.tearDownQuamash()
-
-    def test_generic_tree(self):
-        data = [
-            {
-                'node': {
-                    'title': "Default Profile"
-                },
-                'children': [
-                    {
-                        'node': {
-                            'title': "Test net (inso)"
+def test_generic_tree():
+    data = [
+        {
+            'node': {
+                'title': "Default Profile"
+            },
+            'children': [
+                {
+                    'node': {
+                        'title': "Test net (inso)"
+                    },
+                    'children': [
+                        {
+                            'node': {
+                                'title': "Transactions"
+                            },
+                            'children': []
                         },
-                        'children': [
-                            {
-                                'node': {
-                                    'title': "Transactions"
-                                },
-                                'children': []
+                        {
+                            'node': {
+                                'title': "Network"
                             },
-                            {
-                                'node': {
-                                    'title': "Network"
-                                },
-                                'children': []
-                            }
-                        ]
+                            'children': []
+                        }
+                    ]
+                },
+                {
+                    'node': {
+                        'title': "Le sou"
                     },
-                    {
-                        'node': {
-                            'title': "Le sou"
+                    'children': [
+                        {
+                            'node': {
+                                'title': "Transactions"
+                            },
+                            'children': {}
                         },
-                        'children': [
-                            {
-                                'node': {
-                                    'title': "Transactions"
-                                },
-                                'children': {}
+                        {
+                            'node': {
+                                'title': "Network"
                             },
-                            {
-                                'node': {
-                                    'title': "Network"
-                                },
-                                'children': {
-                                }
+                            'children': {
                             }
-                        ]
-                    }
-                ],
-            }
-        ]
-        tree_model = GenericTreeModel.create("Test", data)
\ No newline at end of file
+                        }
+                    ]
+                }
+            ],
+        }
+    ]
+    tree_model = GenericTreeModel.create("Test", data)
+    assert tree_model.columnCount(QModelIndex()) == 1
+    assert tree_model.rowCount(QModelIndex()) == 1
diff --git a/src/sakia/tests/unit/gui/test_main_window.py b/src/sakia/tests/unit/gui/test_main_window.py
deleted file mode 100644
index b9ac850b..00000000
--- a/src/sakia/tests/unit/gui/test_main_window.py
+++ /dev/null
@@ -1,91 +0,0 @@
-import unittest
-from unittest.mock import patch, MagicMock, Mock, PropertyMock
-from PyQt5.QtCore import QLocale
-from sakia.tests import QuamashTest
-from sakia.gui.mainwindow import MainWindow
-
-
-class TestMainWindow(unittest.TestCase, QuamashTest):
-    def setUp(self):
-        self.setUpQuamash()
-        QLocale.setDefault(QLocale("en_GB"))
-
-        self.identity = Mock(spec='sakia.core.registry.Identity')
-        self.identity.pubkey = "HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk"
-        self.identity.uid = "A"
-
-        self.app = MagicMock(autospec='sakia.core.Application')
-        self.account_joe = Mock(spec='sakia.core.Account')
-        self.account_joe.contacts_changed = Mock()
-        self.account_joe.contacts_changed.disconnect = Mock()
-        self.account_joe.contacts_changed.connect = Mock()
-        self.account_doe = Mock(spec='sakia.core.Account')
-        self.account_doe.contacts_changed = Mock()
-        self.account_doe.contacts_changed.disconnect = Mock()
-        self.account_doe.contacts_changed.connect = Mock()
-
-        def change_current_account(account_name):
-            type(self.app).current_account = PropertyMock(return_value=self.account_doe)
-        self.app.get_account = Mock(side_effect=lambda name: self.app.accounts[name])
-        self.app.change_current_account = Mock(side_effect=change_current_account)
-        type(self.app).current_account = PropertyMock(return_value=self.account_joe)
-        self.app.accounts = {'joe':self.account_joe,
-                             'doe': self.account_doe}
-        self.homescreen = MagicMock(autospec='sakia.gui.homescreen.Homescreen')
-        self.community_view = MagicMock(autospec='sakia.gui.community_view.CommunityView')
-        self.password_asker = MagicMock(autospec='sakia.gui.password_asker.PasswordAsker')
-        self.node_manager = MagicMock(autospec='sakia.gui.node_manager.NodeManager')
-
-    def tearDown(self):
-        self.tearDownQuamash()
-
-    def test_change_account(self):
-        widget = Mock(spec='PyQt5.QtWidgets.QMainWindow', create=True)
-        widget.installEventFilter = Mock()
-        ui = Mock(spec='sakia.presentation.mainwindow_uic.Ui_MainWindow', create=True)
-        ui.setupUi = Mock()
-        label_icon = Mock()
-        label_status = Mock()
-        label_time = Mock()
-        combo_referentials = Mock()
-        combo_referentials.currentIndexChanged = {int: Mock()}
-        mainwindow = MainWindow(self.app, self.account_joe,
-                                self.homescreen, self.community_view, self.node_manager,
-                                widget, ui, label_icon,
-                                label_status, label_time, combo_referentials, self.password_asker)
-        mainwindow.refresh = Mock()
-        mainwindow.action_change_account("doe")
-        self.app.change_current_account.assert_called_once_with(self.account_doe)
-        mainwindow.change_account()
-
-        self.community_view.change_account.assert_called_once_with(self.account_doe, self.password_asker)
-        self.password_asker.change_account.assert_called_once_with(self.account_doe)
-        self.account_joe.contacts_changed.disconnect.assert_called_once_with(mainwindow.refresh_contacts)
-        self.account_doe.contacts_changed.connect.assert_called_once_with(mainwindow.refresh_contacts)
-        mainwindow.refresh.assert_called_once_with()
-
-    def test_change_account_from_none(self):
-        widget = Mock(spec='PyQt5.QtWidgets.QMainWindow', create=True)
-        widget.installEventFilter = Mock()
-        ui = Mock(spec='sakia.presentation.mainwindow_uic.Ui_MainWindow', create=True)
-        ui.setupUi = Mock()
-        label_icon = Mock()
-        label_status = Mock()
-        label_time = Mock()
-        combo_referentials = Mock()
-        combo_referentials.currentIndexChanged = {int: Mock()}
-
-        type(self.app).current_account = PropertyMock(return_value=None)
-        mainwindow = MainWindow(self.app, None, self.homescreen, self.community_view, self.node_manager,
-                                widget, ui, label_icon,
-                                label_status, label_time, combo_referentials, self.password_asker)
-        mainwindow.refresh = Mock()
-        mainwindow.action_change_account("doe")
-        self.app.change_current_account.assert_called_once_with(self.account_doe)
-        mainwindow.change_account()
-
-        self.community_view.change_account.assert_called_once_with(self.account_doe, self.password_asker)
-        self.password_asker.change_account.assert_called_once_with(self.account_doe)
-        self.account_joe.contacts_changed.disconnect.assert_not_called()
-        self.account_doe.contacts_changed.connect.assert_called_once_with(mainwindow.refresh_contacts)
-        mainwindow.refresh.assert_called_once_with()
\ No newline at end of file
diff --git a/src/sakia/tests/unit/core/money/__init__.py b/src/sakia/tests/unit/money/__init__.py
similarity index 100%
rename from src/sakia/tests/unit/core/money/__init__.py
rename to src/sakia/tests/unit/money/__init__.py
diff --git a/src/sakia/tests/unit/core/money/test_quantitative.py b/src/sakia/tests/unit/money/test_quantitative.py
similarity index 100%
rename from src/sakia/tests/unit/core/money/test_quantitative.py
rename to src/sakia/tests/unit/money/test_quantitative.py
diff --git a/src/sakia/tests/unit/core/money/test_quantitative_zsum.py b/src/sakia/tests/unit/money/test_quantitative_zsum.py
similarity index 100%
rename from src/sakia/tests/unit/core/money/test_quantitative_zsum.py
rename to src/sakia/tests/unit/money/test_quantitative_zsum.py
diff --git a/src/sakia/tests/unit/core/money/test_relative.py b/src/sakia/tests/unit/money/test_relative.py
similarity index 100%
rename from src/sakia/tests/unit/core/money/test_relative.py
rename to src/sakia/tests/unit/money/test_relative.py
diff --git a/src/sakia/tests/unit/core/money/test_relative_zsum.py b/src/sakia/tests/unit/money/test_relative_zsum.py
similarity index 100%
rename from src/sakia/tests/unit/core/money/test_relative_zsum.py
rename to src/sakia/tests/unit/money/test_relative_zsum.py
diff --git a/src/sakia/tests/unit/test_decorators.py b/src/sakia/tests/unit/test_decorators.py
index 2e635884..ca38c433 100644
--- a/src/sakia/tests/unit/test_decorators.py
+++ b/src/sakia/tests/unit/test_decorators.py
@@ -1,157 +1,152 @@
 import asyncio
-import unittest
+import pytest
+from sakia.decorators import asyncify, once_at_a_time, cancel_once_task
 
-from PyQt5.QtCore import QLocale
 
-from sakia.decorators import asyncify, once_at_a_time, cancel_once_task
-from sakia.tests import QuamashTest
-
-
-class TestDecorators(unittest.TestCase, QuamashTest):
-    def setUp(self):
-        self.setUpQuamash()
-        QLocale.setDefault(QLocale("en_GB"))
-
-    def tearDown(self):
-        self.tearDownQuamash()
-
-    def test_run_only_once(self):
-        class TaskRunner:
-            def __init__(self):
-                pass
-
-            @once_at_a_time
-            @asyncify
-            async def some_long_task(self, name, callback):
-                await asyncio.sleep(1)
-                callback(name)
-
-        task_runner = TaskRunner()
-        calls = {'A': 0, 'B': 0, 'C': 0}
-
-        def incrementer(name):
-            nonlocal calls
-            calls[name] += 1
-
-        async def exec_test():
-            await asyncio.sleep(3)
-
-        self.lp.call_soon(lambda: task_runner.some_long_task("A", incrementer))
-        self.lp.call_soon(lambda: task_runner.some_long_task("B", incrementer))
-        self.lp.call_soon(lambda: task_runner.some_long_task("C", incrementer))
-        self.lp.run_until_complete(exec_test())
-        self.assertEqual(calls["A"], 0)
-        self.assertEqual(calls["B"], 0)
-        self.assertEqual(calls["C"], 1)
-
-    def test_cancel_once(self):
-        class TaskRunner:
-            def __init__(self):
-                pass
-
-            @once_at_a_time
-            @asyncify
-            async def some_long_task(self, name, callback):
-                await asyncio.sleep(1)
-                callback(name)
-                await asyncio.sleep(1)
-                callback(name)
-
-            def cancel_long_task(self):
-                cancel_once_task(self, self.some_long_task)
-
-        task_runner = TaskRunner()
-        calls = {'A': 0, 'B': 0}
-
-        def incrementer(name):
-            nonlocal calls
-            calls[name] += 1
-
-        async def exec_test():
-            await asyncio.sleep(3)
-
-        self.lp.call_soon(lambda: task_runner.some_long_task("A", incrementer))
-        self.lp.call_soon(lambda: task_runner.some_long_task("B", incrementer))
-        self.lp.call_later(1.5, lambda: task_runner.cancel_long_task())
-        self.lp.run_until_complete(exec_test())
-        self.assertEqual(calls["A"], 0)
-        self.assertEqual(calls["B"], 1)
-
-    def test_cancel_once_two_times(self):
-        class TaskRunner:
-            def __init__(self):
-                pass
-
-            @once_at_a_time
-            @asyncify
-            async def some_long_task(self, name, callback):
-                await asyncio.sleep(1)
-                callback(name)
-                await asyncio.sleep(1)
-                callback(name)
-
-            def cancel_long_task(self):
-                cancel_once_task(self, self.some_long_task)
-
-        task_runner = TaskRunner()
-        calls = {'A': 0, 'B': 0, 'C': 0, 'D': 0}
-
-        def incrementer(name):
-            nonlocal calls
-            calls[name] += 1
-
-        async def exec_test():
-            await asyncio.sleep(6)
-
-        self.lp.call_soon(lambda: task_runner.some_long_task("A", incrementer))
-        self.lp.call_soon(lambda: task_runner.some_long_task("B", incrementer))
-        self.lp.call_later(1.5, lambda: task_runner.cancel_long_task())
-        self.lp.call_later(2, lambda: task_runner.some_long_task("C", incrementer))
-        self.lp.call_later(2.1, lambda: task_runner.some_long_task("D", incrementer))
-        self.lp.call_later(3.5, lambda: task_runner.cancel_long_task())
-        self.lp.run_until_complete(exec_test())
-        self.assertEqual(calls["A"], 0)
-        self.assertEqual(calls["B"], 1)
-        self.assertEqual(calls["C"], 0)
-        self.assertEqual(calls["D"], 1)
-
-    def test_two_runners(self):
-        class TaskRunner:
-            def __init__(self, name):
-                self.some_long_task(name, incrementer)
-
-            @classmethod
-            def create(cls, name):
-                return cls(name)
-
-            @once_at_a_time
-            @asyncify
-            async def some_long_task(self, name, callback):
-                await asyncio.sleep(1)
-                callback(name)
-                await asyncio.sleep(1)
-                callback(name)
-
-            def cancel_long_task(self):
-                cancel_once_task(self, self.some_long_task)
-
-        calls = {'A': 0, 'B': 0, 'C': 0}
-
-        def incrementer(name):
-            nonlocal calls
-            calls[name] += 1
-
-        async def exec_test():
-            tr1 = TaskRunner.create("A")
-            tr2 = TaskRunner.create("B")
-            tr3 = TaskRunner.create("C")
-            await asyncio.sleep(1.5)
-            tr1.some_long_task("A", incrementer)
-            tr2.some_long_task("B", incrementer)
-            tr3.some_long_task("C", incrementer)
-            await asyncio.sleep(1.5)
-
-        self.lp.run_until_complete(exec_test())
-        self.assertEqual(calls["A"], 2)
-        self.assertEqual(calls["B"], 2)
-        self.assertEqual(calls["C"], 2)
+@pytest.mark.asyncio
+async def test_run_only_once():
+    class TaskRunner:
+        def __init__(self):
+            pass
+
+        @once_at_a_time
+        @asyncify
+        async def some_long_task(self, name, callback):
+            await asyncio.sleep(1)
+            callback(name)
+
+    task_runner = TaskRunner()
+    calls = {'A': 0, 'B': 0, 'C': 0}
+
+    def incrementer(name):
+        nonlocal calls
+        calls[name] += 1
+
+    async def exec_test():
+        await asyncio.sleep(3)
+
+    asyncio.ensure_future(task_runner.some_long_task("A", incrementer))
+    asyncio.ensure_future(task_runner.some_long_task("B", incrementer))
+    asyncio.ensure_future(task_runner.some_long_task("C", incrementer))
+    await exec_test()
+    assert calls["A"] == 0
+    assert calls["B"] == 0
+    assert calls["C"] == 1
+
+
+@pytest.mark.asyncio
+async def test_cancel_once(application):
+    class TaskRunner:
+        def __init__(self):
+            pass
+
+        @once_at_a_time
+        @asyncify
+        async def some_long_task(self, name, callback):
+            await asyncio.sleep(1)
+            callback(name)
+            await asyncio.sleep(1)
+            callback(name)
+
+        def cancel_long_task(self):
+            cancel_once_task(self, self.some_long_task)
+
+    task_runner = TaskRunner()
+    calls = {'A': 0, 'B': 0}
+
+    def incrementer(name):
+        nonlocal calls
+        calls[name] += 1
+
+    async def exec_test():
+        await asyncio.sleep(3)
+
+    application.loop.call_soon(lambda: task_runner.some_long_task("A", incrementer))
+    application.loop.call_soon(lambda: task_runner.some_long_task("B", incrementer))
+    application.loop.call_later(1.5, lambda: task_runner.cancel_long_task())
+    await exec_test()
+    assert calls["A"] == 0
+    assert calls["B"] == 1
+
+
+@pytest.mark.asyncio
+async def test_cancel_once_two_times(application):
+    class TaskRunner:
+        def __init__(self):
+            pass
+
+        @once_at_a_time
+        @asyncify
+        async def some_long_task(self, name, callback):
+            await asyncio.sleep(1)
+            callback(name)
+            await asyncio.sleep(1)
+            callback(name)
+
+        def cancel_long_task(self):
+            cancel_once_task(self, self.some_long_task)
+
+    task_runner = TaskRunner()
+    calls = {'A': 0, 'B': 0, 'C': 0, 'D': 0}
+
+    def incrementer(name):
+        nonlocal calls
+        calls[name] += 1
+
+    async def exec_test():
+        await asyncio.sleep(6)
+
+    application.loop.call_soon(lambda: task_runner.some_long_task("A", incrementer))
+    application.loop.call_soon(lambda: task_runner.some_long_task("B", incrementer))
+    application.loop.call_later(1.5, lambda: task_runner.cancel_long_task())
+    application.loop.call_later(2, lambda: task_runner.some_long_task("C", incrementer))
+    application.loop.call_later(2.1, lambda: task_runner.some_long_task("D", incrementer))
+    application.loop.call_later(3.5, lambda: task_runner.cancel_long_task())
+    await exec_test()
+    assert calls["A"] == 0
+    assert calls["B"] == 1
+    assert calls["C"] == 0
+    assert calls["D"] == 1
+
+
+@pytest.mark.asyncio
+async def test_two_runners():
+    class TaskRunner:
+        def __init__(self, name):
+            self.some_long_task(name, incrementer)
+
+        @classmethod
+        def create(cls, name):
+            return cls(name)
+
+        @once_at_a_time
+        @asyncify
+        async def some_long_task(self, name, callback):
+            await asyncio.sleep(1)
+            callback(name)
+            await asyncio.sleep(1)
+            callback(name)
+
+        def cancel_long_task(self):
+            cancel_once_task(self, self.some_long_task)
+
+    calls = {'A': 0, 'B': 0, 'C': 0}
+
+    def incrementer(name):
+        nonlocal calls
+        calls[name] += 1
+
+    async def exec_test():
+        tr1 = TaskRunner.create("A")
+        tr2 = TaskRunner.create("B")
+        tr3 = TaskRunner.create("C")
+        await asyncio.sleep(1.5)
+        tr1.some_long_task("A", incrementer)
+        tr2.some_long_task("B", incrementer)
+        tr3.some_long_task("C", incrementer)
+        await asyncio.sleep(1.5)
+
+    await exec_test()
+    assert calls["A"] == 2
+    assert calls["B"] == 2
+    assert calls["C"] == 2
-- 
GitLab