Example #1
0
class StreamManagerComponent(Component):
    component_name = STREAM_MANAGER_COMPONENT
    depends_on = [BLOB_COMPONENT, DATABASE_COMPONENT, WALLET_COMPONENT]

    def __init__(self, component_manager):
        super().__init__(component_manager)
        self.stream_manager: StreamManager = None

    @property
    def component(self) -> typing.Optional[StreamManager]:
        return self.stream_manager

    async def get_status(self):
        if not self.stream_manager:
            return
        return {'managed_files': len(self.stream_manager.streams)}

    async def start(self):
        blob_manager = self.component_manager.get_component(BLOB_COMPONENT)
        storage = self.component_manager.get_component(DATABASE_COMPONENT)
        wallet = self.component_manager.get_component(WALLET_COMPONENT)
        try:
            node = self.component_manager.get_component(DHT_COMPONENT)
        except NameError:
            node = None
        log.info('Starting the file manager')
        loop = asyncio.get_event_loop()
        self.stream_manager = StreamManager(
            loop, self.conf, blob_manager, wallet, storage, node,
            self.component_manager.analytics_manager)
        await self.stream_manager.start()
        log.info('Done setting up file manager')

    async def stop(self):
        self.stream_manager.stop()
Example #2
0
 async def start(self):
     blob_manager = self.component_manager.get_component(BLOB_COMPONENT)
     storage = self.component_manager.get_component(DATABASE_COMPONENT)
     wallet = self.component_manager.get_component(WALLET_COMPONENT)
     try:
         node = self.component_manager.get_component(DHT_COMPONENT)
     except NameError:
         node = None
     log.info('Starting the file manager')
     loop = asyncio.get_event_loop()
     self.stream_manager = StreamManager(
         loop, self.conf, blob_manager, wallet, storage, node,
         self.component_manager.analytics_manager)
     await self.stream_manager.start()
     log.info('Done setting up file manager')
Example #3
0
 async def setup_stream_manager(self, balance=10.0, fee=None, old_sort=False):
     file_path = os.path.join(self.server_dir, "test_file")
     with open(file_path, 'wb') as f:
         f.write(os.urandom(20000000))
     descriptor = await StreamDescriptor.create_stream(
         self.loop, self.server_blob_manager.blob_dir, file_path, old_sort=old_sort
     )
     self.sd_hash = descriptor.sd_hash
     self.mock_wallet, self.uri = get_mock_wallet(self.sd_hash, self.client_storage, balance, fee)
     self.stream_manager = StreamManager(self.loop, self.client_config, self.client_blob_manager, self.mock_wallet,
                                         self.client_storage, get_mock_node(self.server_from_client),
                                         AnalyticsManager(self.client_config,
                                                          binascii.hexlify(generate_id()).decode(),
                                                          binascii.hexlify(generate_id()).decode()))
     self.exchange_rate_manager = get_dummy_exchange_rate_manager(time)
Example #4
0
    async def asyncSetUp(self):
        self.loop = asyncio.get_event_loop()
        self.key = b'deadbeef' * 4
        self.cleartext = os.urandom(20000000)

        tmp_dir = tempfile.mkdtemp()
        self.addCleanup(lambda: shutil.rmtree(tmp_dir))
        self.conf = Config()
        self.storage = SQLiteStorage(self.conf, os.path.join(tmp_dir, "lbrynet.sqlite"))
        await self.storage.open()
        self.blob_manager = BlobManager(self.loop, tmp_dir, self.storage, self.conf)
        self.stream_manager = StreamManager(self.loop, Config(), self.blob_manager, None, self.storage, None)

        server_tmp_dir = tempfile.mkdtemp()
        self.addCleanup(lambda: shutil.rmtree(server_tmp_dir))
        self.server_conf = Config()
        self.server_storage = SQLiteStorage(self.server_conf, os.path.join(server_tmp_dir, "lbrynet.sqlite"))
        await self.server_storage.open()
        self.server_blob_manager = BlobManager(self.loop, server_tmp_dir, self.server_storage, self.server_conf)

        download_dir = tempfile.mkdtemp()
        self.addCleanup(lambda: shutil.rmtree(download_dir))

        # create the stream
        file_path = os.path.join(tmp_dir, "test_file")
        with open(file_path, 'wb') as f:
            f.write(self.cleartext)

        self.stream = await self.stream_manager.create_stream(file_path)
Example #5
0
 async def asyncSetUp(self):
     await super().asyncSetUp()
     file_path = os.path.join(self.server_dir, "test_file")
     with open(file_path, 'wb') as f:
         f.write(os.urandom(20000000))
     descriptor = await StreamDescriptor.create_stream(
         self.loop, self.server_blob_manager.blob_dir, file_path)
     self.sd_hash = descriptor.calculate_sd_hash()
     self.mock_wallet, self.uri = get_mock_wallet(self.sd_hash,
                                                  self.client_storage)
     self.stream_manager = StreamManager(
         self.loop, self.client_config, self.client_blob_manager,
         self.mock_wallet, self.client_storage,
         get_mock_node(self.server_from_client))
     self.exchange_rate_manager = get_dummy_exchange_rate_manager(time)
Example #6
0
    async def test_create_managed_stream_announces(self):
        # setup a blob manager
        storage = SQLiteStorage(Config(), ":memory:")
        await storage.open()
        tmp_dir = tempfile.mkdtemp()
        self.addCleanup(lambda: shutil.rmtree(tmp_dir))
        blob_manager = BlobFileManager(self.loop, tmp_dir, storage)
        stream_manager = StreamManager(self.loop, Config(), blob_manager, None, storage, None)
        # create the stream
        download_dir = tempfile.mkdtemp()
        self.addCleanup(lambda: shutil.rmtree(download_dir))
        file_path = os.path.join(download_dir, "test_file")
        with open(file_path, 'wb') as f:
            f.write(b'testtest')

        stream = await stream_manager.create_stream(file_path)
        self.assertEqual(
            [stream.sd_hash, stream.descriptor.blobs[0].blob_hash],
            await storage.get_blobs_to_announce())
Example #7
0
 async def start(self):
     blob_manager = self.component_manager.get_component(BLOB_COMPONENT)
     storage = self.component_manager.get_component(DATABASE_COMPONENT)
     wallet = self.component_manager.get_component(WALLET_COMPONENT)
     try:
         node = self.component_manager.get_component(DHT_COMPONENT)
     except NameError:
         node = None
     log.info('Starting the file manager')
     loop = asyncio.get_event_loop()
     self.stream_manager = StreamManager(
         loop, blob_manager, wallet, storage, node,
         self.conf.blob_download_timeout, self.conf.peer_connect_timeout, [
             KademliaPeer(loop,
                          address=(await resolve_host(loop, url)),
                          tcp_port=port + 1)
             for url, port in self.conf.reflector_servers
         ], self.conf.reflector_servers)
     await self.stream_manager.start()
     log.info('Done setting up file manager')
Example #8
0
class TestStreamManager(BlobExchangeTestBase):
    async def setup_stream_manager(self,
                                   balance=10.0,
                                   fee=None,
                                   old_sort=False):
        file_path = os.path.join(self.server_dir, "test_file")
        with open(file_path, 'wb') as f:
            f.write(os.urandom(20000000))
        descriptor = await StreamDescriptor.create_stream(
            self.loop,
            self.server_blob_manager.blob_dir,
            file_path,
            old_sort=old_sort)
        self.sd_hash = descriptor.sd_hash
        self.mock_wallet, self.uri = get_mock_wallet(self.sd_hash,
                                                     self.client_storage,
                                                     balance, fee)
        self.stream_manager = StreamManager(
            self.loop, self.client_config, self.client_blob_manager,
            self.mock_wallet, self.client_storage,
            get_mock_node(self.server_from_client),
            AnalyticsManager(self.client_config,
                             binascii.hexlify(generate_id()).decode(),
                             binascii.hexlify(generate_id()).decode()))
        self.exchange_rate_manager = get_dummy_exchange_rate_manager(time)

    async def _test_time_to_first_bytes(self,
                                        check_post,
                                        error=None,
                                        after_setup=None):
        await self.setup_stream_manager()
        if after_setup:
            after_setup()
        checked_analytics_event = False

        async def _check_post(event):
            check_post(event)
            nonlocal checked_analytics_event
            checked_analytics_event = True

        self.stream_manager.analytics_manager._post = _check_post
        if error:
            with self.assertRaises(error):
                await self.stream_manager.download_stream_from_uri(
                    self.uri, self.exchange_rate_manager)
        else:
            await self.stream_manager.download_stream_from_uri(
                self.uri, self.exchange_rate_manager)
        await asyncio.sleep(0, loop=self.loop)
        self.assertTrue(checked_analytics_event)

    async def test_time_to_first_bytes(self):
        def check_post(event):
            self.assertEqual(event['event'], 'Time To First Bytes')
            total_duration = event['properties']['total_duration']
            resolve_duration = event['properties']['resolve_duration']
            head_blob_duration = event['properties']['head_blob_duration']
            sd_blob_duration = event['properties']['sd_blob_duration']
            self.assertFalse(event['properties']['added_fixed_peers'])
            self.assertTrue(total_duration >= resolve_duration +
                            head_blob_duration + sd_blob_duration)

        await self._test_time_to_first_bytes(check_post)

    async def test_fixed_peer_delay_dht_peers_found(self):
        self.client_config.reflector_servers = [
            (self.server_from_client.address,
             self.server_from_client.tcp_port - 1)
        ]
        server_from_client = None
        self.server_from_client, server_from_client = server_from_client, self.server_from_client

        def after_setup():
            self.stream_manager.node.protocol.routing_table.get_peers = lambda: [
                server_from_client
            ]

        def check_post(event):
            self.assertEqual(event['event'], 'Time To First Bytes')
            total_duration = event['properties']['total_duration']
            resolve_duration = event['properties']['resolve_duration']
            head_blob_duration = event['properties']['head_blob_duration']
            sd_blob_duration = event['properties']['sd_blob_duration']

            self.assertEqual(event['event'], 'Time To First Bytes')
            self.assertEqual(event['properties']['tried_peers_count'], 1)
            self.assertEqual(event['properties']['active_peer_count'], 1)
            self.assertEqual(event['properties']['use_fixed_peers'], True)
            self.assertEqual(event['properties']['added_fixed_peers'], True)
            self.assertEqual(event['properties']['fixed_peer_delay'],
                             self.client_config.fixed_peer_delay)
            self.assertGreaterEqual(
                total_duration,
                resolve_duration + head_blob_duration + sd_blob_duration)

        await self._test_time_to_first_bytes(check_post,
                                             after_setup=after_setup)

    async def test_override_fixed_peer_delay_dht_disabled(self):
        self.client_config.reflector_servers = [
            (self.server_from_client.address,
             self.server_from_client.tcp_port - 1)
        ]
        self.client_config.components_to_skip = ['dht', 'hash_announcer']
        self.client_config.fixed_peer_delay = 9001.0
        self.server_from_client = None

        def check_post(event):
            total_duration = event['properties']['total_duration']
            resolve_duration = event['properties']['resolve_duration']
            head_blob_duration = event['properties']['head_blob_duration']
            sd_blob_duration = event['properties']['sd_blob_duration']

            self.assertEqual(event['event'], 'Time To First Bytes')
            self.assertEqual(event['properties']['tried_peers_count'], 1)
            self.assertEqual(event['properties']['active_peer_count'], 1)
            self.assertEqual(event['properties']['use_fixed_peers'], True)
            self.assertEqual(event['properties']['added_fixed_peers'], True)
            self.assertEqual(event['properties']['fixed_peer_delay'], 0.0)
            self.assertGreaterEqual(
                total_duration,
                resolve_duration + head_blob_duration + sd_blob_duration)

        start = self.loop.time()
        await self._test_time_to_first_bytes(check_post)
        self.assertTrue(self.loop.time() - start < 3)

    async def test_no_peers_timeout(self):
        # FIXME: the download should ideally fail right away if there are no peers
        # to initialize the shortlist and fixed peers are disabled
        self.server_from_client = None
        self.client_config.download_timeout = 3.0

        def check_post(event):
            self.assertEqual(event['event'], 'Time To First Bytes')
            self.assertEqual(event['properties']['error'], 'DownloadSDTimeout')
            self.assertEqual(event['properties']['tried_peers_count'], None)
            self.assertEqual(event['properties']['active_peer_count'], None)
            self.assertEqual(event['properties']['use_fixed_peers'], False)
            self.assertEqual(event['properties']['added_fixed_peers'], False)
            self.assertEqual(event['properties']['fixed_peer_delay'], None)

        start = self.loop.time()
        await self._test_time_to_first_bytes(check_post, DownloadSDTimeout)
        duration = self.loop.time() - start
        self.assertTrue(4.0 >= duration >= 3.0)

    async def test_download_stop_resume_delete(self):
        await self.setup_stream_manager()
        received = []
        expected_events = ['Time To First Bytes', 'Download Finished']

        async def check_post(event):
            received.append(event['event'])

        self.stream_manager.analytics_manager._post = check_post

        self.assertSetEqual(self.stream_manager.streams, set())
        stream = await self.stream_manager.download_stream_from_uri(
            self.uri, self.exchange_rate_manager)
        stream_hash = stream.stream_hash
        self.assertSetEqual(self.stream_manager.streams, {stream})
        self.assertTrue(stream.running)
        self.assertFalse(stream.finished)
        self.assertTrue(
            os.path.isfile(os.path.join(self.client_dir, "test_file")))
        stored_status = await self.client_storage.run_and_return_one_or_none(
            "select status from file where stream_hash=?", stream_hash)
        self.assertEqual(stored_status, "running")

        await self.stream_manager.stop_stream(stream)

        self.assertFalse(stream.finished)
        self.assertFalse(stream.running)
        self.assertFalse(
            os.path.isfile(os.path.join(self.client_dir, "test_file")))
        stored_status = await self.client_storage.run_and_return_one_or_none(
            "select status from file where stream_hash=?", stream_hash)
        self.assertEqual(stored_status, "stopped")

        await self.stream_manager.start_stream(stream)
        await stream.downloader.stream_finished_event.wait()
        await asyncio.sleep(0, loop=self.loop)
        self.assertTrue(stream.finished)
        self.assertFalse(stream.running)
        self.assertTrue(
            os.path.isfile(os.path.join(self.client_dir, "test_file")))
        stored_status = await self.client_storage.run_and_return_one_or_none(
            "select status from file where stream_hash=?", stream_hash)
        self.assertEqual(stored_status, "finished")

        await self.stream_manager.delete_stream(stream, True)
        self.assertSetEqual(self.stream_manager.streams, set())
        self.assertFalse(
            os.path.isfile(os.path.join(self.client_dir, "test_file")))
        stored_status = await self.client_storage.run_and_return_one_or_none(
            "select status from file where stream_hash=?", stream_hash)
        self.assertEqual(stored_status, None)
        self.assertListEqual(expected_events, received)

    async def _test_download_error_on_start(self,
                                            expected_error,
                                            timeout=None):
        with self.assertRaises(expected_error):
            await self.stream_manager.download_stream_from_uri(
                self.uri, self.exchange_rate_manager, timeout=timeout)

    async def _test_download_error_analytics_on_start(self,
                                                      expected_error,
                                                      timeout=None):
        received = []

        async def check_post(event):
            self.assertEqual("Time To First Bytes", event['event'])
            received.append(event['properties']['error'])

        self.stream_manager.analytics_manager._post = check_post
        await self._test_download_error_on_start(expected_error, timeout)
        await asyncio.sleep(0, loop=self.loop)
        self.assertListEqual([expected_error.__name__], received)

    async def test_insufficient_funds(self):
        fee = {
            'currency': 'LBC',
            'amount': 11.0,
            'address': 'bYFeMtSL7ARuG1iMpjFyrnTe4oJHSAVNXF',
            'version': '_0_0_1'
        }
        await self.setup_stream_manager(10.0, fee)
        await self._test_download_error_on_start(InsufficientFundsError)

    async def test_fee_above_max_allowed(self):
        fee = {
            'currency': 'USD',
            'amount': 51.0,
            'address': 'bYFeMtSL7ARuG1iMpjFyrnTe4oJHSAVNXF',
            'version': '_0_0_1'
        }
        await self.setup_stream_manager(1000000.0, fee)
        await self._test_download_error_on_start(KeyFeeAboveMaxAllowed)

    async def test_resolve_error(self):
        await self.setup_stream_manager()
        self.uri = "fake"
        await self._test_download_error_on_start(ResolveError)

    async def test_download_sd_timeout(self):
        self.server.stop_server()
        await self.setup_stream_manager()
        await self._test_download_error_analytics_on_start(DownloadSDTimeout,
                                                           timeout=1)

    async def test_download_data_timeout(self):
        await self.setup_stream_manager()
        with open(os.path.join(self.server_dir, self.sd_hash), 'r') as sdf:
            head_blob_hash = json.loads(sdf.read())['blobs'][0]['blob_hash']
        self.server_blob_manager.delete_blob(head_blob_hash)
        await self._test_download_error_analytics_on_start(DownloadDataTimeout,
                                                           timeout=1)

    async def test_download_then_recover_stream_on_startup(
            self, old_sort=False):
        expected_analytics_events = [
            'Time To First Bytes', 'Download Finished'
        ]
        received_events = []

        async def check_post(event):
            received_events.append(event['event'])

        await self.setup_stream_manager(old_sort=old_sort)
        self.stream_manager.analytics_manager._post = check_post

        self.assertSetEqual(self.stream_manager.streams, set())
        stream = await self.stream_manager.download_stream_from_uri(
            self.uri, self.exchange_rate_manager)
        await stream.downloader.stream_finished_event.wait()
        await asyncio.sleep(0, loop=self.loop)
        self.stream_manager.stop()
        self.client_blob_manager.stop()
        os.remove(
            os.path.join(self.client_blob_manager.blob_dir, stream.sd_hash))
        for blob in stream.descriptor.blobs[:-1]:
            os.remove(
                os.path.join(self.client_blob_manager.blob_dir,
                             blob.blob_hash))
        await self.client_blob_manager.setup()
        await self.stream_manager.start()
        self.assertEqual(1, len(self.stream_manager.streams))
        self.assertEqual(stream.sd_hash,
                         list(self.stream_manager.streams)[0].sd_hash)
        self.assertEqual('stopped',
                         list(self.stream_manager.streams)[0].status)

        sd_blob = self.client_blob_manager.get_blob(stream.sd_hash)
        self.assertTrue(sd_blob.file_exists)
        self.assertTrue(sd_blob.get_is_verified())
        self.assertListEqual(expected_analytics_events, received_events)

    def test_download_then_recover_old_sort_stream_on_startup(self):
        return self.test_download_then_recover_stream_on_startup(old_sort=True)
class TestStreamManager(BlobExchangeTestBase):
    async def setup_stream_manager(self,
                                   balance=10.0,
                                   fee=None,
                                   old_sort=False):
        file_path = os.path.join(self.server_dir, "test_file")
        with open(file_path, 'wb') as f:
            f.write(os.urandom(20000000))
        descriptor = await StreamDescriptor.create_stream(
            self.loop,
            self.server_blob_manager.blob_dir,
            file_path,
            old_sort=old_sort)
        self.sd_hash = descriptor.sd_hash
        self.mock_wallet, self.uri = get_mock_wallet(self.sd_hash,
                                                     self.client_storage,
                                                     balance, fee)
        self.stream_manager = StreamManager(
            self.loop, self.client_config, self.client_blob_manager,
            self.mock_wallet, self.client_storage,
            get_mock_node(self.server_from_client))
        self.exchange_rate_manager = get_dummy_exchange_rate_manager(time)

    async def test_download_stop_resume_delete(self):
        await self.setup_stream_manager()
        self.assertSetEqual(self.stream_manager.streams, set())
        stream = await self.stream_manager.download_stream_from_uri(
            self.uri, self.exchange_rate_manager)
        stream_hash = stream.stream_hash
        self.assertSetEqual(self.stream_manager.streams, {stream})
        self.assertTrue(stream.running)
        self.assertFalse(stream.finished)
        self.assertTrue(
            os.path.isfile(os.path.join(self.client_dir, "test_file")))
        stored_status = await self.client_storage.run_and_return_one_or_none(
            "select status from file where stream_hash=?", stream_hash)
        self.assertEqual(stored_status, "running")

        await self.stream_manager.stop_stream(stream)

        self.assertFalse(stream.finished)
        self.assertFalse(stream.running)
        self.assertFalse(
            os.path.isfile(os.path.join(self.client_dir, "test_file")))
        stored_status = await self.client_storage.run_and_return_one_or_none(
            "select status from file where stream_hash=?", stream_hash)
        self.assertEqual(stored_status, "stopped")

        await self.stream_manager.start_stream(stream)
        await stream.downloader.stream_finished_event.wait()
        await asyncio.sleep(0.01)
        self.assertTrue(stream.finished)
        self.assertFalse(stream.running)
        self.assertTrue(
            os.path.isfile(os.path.join(self.client_dir, "test_file")))
        stored_status = await self.client_storage.run_and_return_one_or_none(
            "select status from file where stream_hash=?", stream_hash)
        self.assertEqual(stored_status, "finished")

        await self.stream_manager.delete_stream(stream, True)
        self.assertSetEqual(self.stream_manager.streams, set())
        self.assertFalse(
            os.path.isfile(os.path.join(self.client_dir, "test_file")))
        stored_status = await self.client_storage.run_and_return_one_or_none(
            "select status from file where stream_hash=?", stream_hash)
        self.assertEqual(stored_status, None)

    async def test_insufficient_funds(self):
        fee = {
            'currency': 'LBC',
            'amount': 11.0,
            'address': 'bYFeMtSL7ARuG1iMpjFyrnTe4oJHSAVNXF',
            'version': '_0_0_1'
        }
        await self.setup_stream_manager(10.0, fee)
        with self.assertRaises(InsufficientFundsError):
            await self.stream_manager.download_stream_from_uri(
                self.uri, self.exchange_rate_manager)

    async def test_fee_above_max_allowed(self):
        fee = {
            'currency': 'USD',
            'amount': 51.0,
            'address': 'bYFeMtSL7ARuG1iMpjFyrnTe4oJHSAVNXF',
            'version': '_0_0_1'
        }
        await self.setup_stream_manager(1000000.0, fee)
        with self.assertRaises(KeyFeeAboveMaxAllowed):
            await self.stream_manager.download_stream_from_uri(
                self.uri, self.exchange_rate_manager)

    async def test_download_then_recover_stream_on_startup(
            self, old_sort=False):
        await self.setup_stream_manager(old_sort=old_sort)
        self.assertSetEqual(self.stream_manager.streams, set())
        stream = await self.stream_manager.download_stream_from_uri(
            self.uri, self.exchange_rate_manager)
        await stream.downloader.stream_finished_event.wait()
        self.stream_manager.stop()
        self.client_blob_manager.stop()
        os.remove(
            os.path.join(self.client_blob_manager.blob_dir, stream.sd_hash))
        for blob in stream.descriptor.blobs[:-1]:
            os.remove(
                os.path.join(self.client_blob_manager.blob_dir,
                             blob.blob_hash))
        await self.client_blob_manager.setup()
        await self.stream_manager.start()
        self.assertEqual(1, len(self.stream_manager.streams))
        self.assertEqual(stream.sd_hash,
                         list(self.stream_manager.streams)[0].sd_hash)
        self.assertEqual('stopped',
                         list(self.stream_manager.streams)[0].status)

        sd_blob = self.client_blob_manager.get_blob(stream.sd_hash)
        self.assertTrue(sd_blob.file_exists)
        self.assertTrue(sd_blob.get_is_verified())

    def test_download_then_recover_old_sort_stream_on_startup(self):
        return self.test_download_then_recover_stream_on_startup(old_sort=True)
Example #10
0
class TestStreamManager(BlobExchangeTestBase):
    async def setup_stream_manager(self,
                                   balance=10.0,
                                   fee=None,
                                   old_sort=False):
        file_path = os.path.join(self.server_dir, "test_file")
        with open(file_path, 'wb') as f:
            f.write(os.urandom(20000000))
        descriptor = await StreamDescriptor.create_stream(
            self.loop,
            self.server_blob_manager.blob_dir,
            file_path,
            old_sort=old_sort)
        self.sd_hash = descriptor.sd_hash
        self.mock_wallet, self.uri = get_mock_wallet(self.sd_hash,
                                                     self.client_storage,
                                                     balance, fee)
        self.stream_manager = StreamManager(
            self.loop, self.client_config, self.client_blob_manager,
            self.mock_wallet, self.client_storage,
            get_mock_node(self.server_from_client),
            AnalyticsManager(self.client_config,
                             binascii.hexlify(generate_id()).decode(),
                             binascii.hexlify(generate_id()).decode()))
        self.exchange_rate_manager = get_dummy_exchange_rate_manager(time)

    async def test_download_stop_resume_delete(self):
        await self.setup_stream_manager()
        received = []
        expected_events = ['Download Started', 'Download Finished']

        async def check_post(event):
            received.append(event['event'])

        self.stream_manager.analytics_manager._post = check_post

        self.assertSetEqual(self.stream_manager.streams, set())
        stream = await self.stream_manager.download_stream_from_uri(
            self.uri, self.exchange_rate_manager)
        stream_hash = stream.stream_hash
        self.assertSetEqual(self.stream_manager.streams, {stream})
        self.assertTrue(stream.running)
        self.assertFalse(stream.finished)
        self.assertTrue(
            os.path.isfile(os.path.join(self.client_dir, "test_file")))
        stored_status = await self.client_storage.run_and_return_one_or_none(
            "select status from file where stream_hash=?", stream_hash)
        self.assertEqual(stored_status, "running")

        await self.stream_manager.stop_stream(stream)

        self.assertFalse(stream.finished)
        self.assertFalse(stream.running)
        self.assertFalse(
            os.path.isfile(os.path.join(self.client_dir, "test_file")))
        stored_status = await self.client_storage.run_and_return_one_or_none(
            "select status from file where stream_hash=?", stream_hash)
        self.assertEqual(stored_status, "stopped")

        await self.stream_manager.start_stream(stream)
        await stream.downloader.stream_finished_event.wait()
        await asyncio.sleep(0.01)
        self.assertTrue(stream.finished)
        self.assertFalse(stream.running)
        self.assertTrue(
            os.path.isfile(os.path.join(self.client_dir, "test_file")))
        stored_status = await self.client_storage.run_and_return_one_or_none(
            "select status from file where stream_hash=?", stream_hash)
        self.assertEqual(stored_status, "finished")

        await self.stream_manager.delete_stream(stream, True)
        self.assertSetEqual(self.stream_manager.streams, set())
        self.assertFalse(
            os.path.isfile(os.path.join(self.client_dir, "test_file")))
        stored_status = await self.client_storage.run_and_return_one_or_none(
            "select status from file where stream_hash=?", stream_hash)
        self.assertEqual(stored_status, None)
        self.assertListEqual(expected_events, received)

    async def _test_download_error(self, expected_error):
        received = []

        async def check_post(event):
            self.assertEqual("Download Errored", event['event'])
            received.append(event['properties']['error'])

        self.stream_manager.analytics_manager._post = check_post

        with self.assertRaises(expected_error):
            await self.stream_manager.download_stream_from_uri(
                self.uri, self.exchange_rate_manager)
        self.assertListEqual([expected_error.__name__], received)

    async def test_insufficient_funds(self):
        fee = {
            'currency': 'LBC',
            'amount': 11.0,
            'address': 'bYFeMtSL7ARuG1iMpjFyrnTe4oJHSAVNXF',
            'version': '_0_0_1'
        }
        await self.setup_stream_manager(10.0, fee)
        await self._test_download_error(InsufficientFundsError)

    async def test_fee_above_max_allowed(self):
        fee = {
            'currency': 'USD',
            'amount': 51.0,
            'address': 'bYFeMtSL7ARuG1iMpjFyrnTe4oJHSAVNXF',
            'version': '_0_0_1'
        }
        await self.setup_stream_manager(1000000.0, fee)
        await self._test_download_error(KeyFeeAboveMaxAllowed)

    async def test_resolve_error(self):
        await self.setup_stream_manager()
        self.uri = "fake"
        await self._test_download_error(ResolveError)

    async def test_download_timeout(self):
        self.server.stop_server()
        self.client_config.download_timeout = 1.0
        await self.setup_stream_manager()
        await self._test_download_error(DownloadSDTimeout)

    async def test_download_then_recover_stream_on_startup(
            self, old_sort=False):
        expected_analytics_events = ['Download Started', 'Download Finished']
        received_events = []

        async def check_post(event):
            received_events.append(event['event'])

        await self.setup_stream_manager(old_sort=old_sort)
        self.stream_manager.analytics_manager._post = check_post

        self.assertSetEqual(self.stream_manager.streams, set())
        stream = await self.stream_manager.download_stream_from_uri(
            self.uri, self.exchange_rate_manager)
        await stream.downloader.stream_finished_event.wait()
        self.stream_manager.stop()
        self.client_blob_manager.stop()
        os.remove(
            os.path.join(self.client_blob_manager.blob_dir, stream.sd_hash))
        for blob in stream.descriptor.blobs[:-1]:
            os.remove(
                os.path.join(self.client_blob_manager.blob_dir,
                             blob.blob_hash))
        await self.client_blob_manager.setup()
        await self.stream_manager.start()
        self.assertEqual(1, len(self.stream_manager.streams))
        self.assertEqual(stream.sd_hash,
                         list(self.stream_manager.streams)[0].sd_hash)
        self.assertEqual('stopped',
                         list(self.stream_manager.streams)[0].status)

        sd_blob = self.client_blob_manager.get_blob(stream.sd_hash)
        self.assertTrue(sd_blob.file_exists)
        self.assertTrue(sd_blob.get_is_verified())
        self.assertListEqual(expected_analytics_events, received_events)

    def test_download_then_recover_old_sort_stream_on_startup(self):
        return self.test_download_then_recover_stream_on_startup(old_sort=True)