From 1d8b32abc821116da4f3b4f192bc06a931d30076 Mon Sep 17 00:00:00 2001
From: drebs <drebs@leap.se>
Date: Wed, 29 Nov 2017 11:42:29 -0200
Subject: [feature] add priorities for blob transfers

Closes: #8691
---
 tests/blobs/test_priorities.py | 207 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 207 insertions(+)
 create mode 100644 tests/blobs/test_priorities.py

(limited to 'tests/blobs')

diff --git a/tests/blobs/test_priorities.py b/tests/blobs/test_priorities.py
new file mode 100644
index 00000000..fca582e5
--- /dev/null
+++ b/tests/blobs/test_priorities.py
@@ -0,0 +1,207 @@
+# -*- coding: utf-8 -*-
+# test_priorities.py
+# Copyright (C) 2017 LEAP
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+"""
+Tests for transfer priorities.
+"""
+import pytest
+
+from io import BytesIO
+from mock import Mock
+from twisted.internet import defer
+from twisted.trial import unittest
+from uuid import uuid4
+
+from leap.soledad.client._db.blobs import BlobManager
+from leap.soledad.client._db.blobs import Priority
+from leap.soledad.client._db.blobs import SyncStatus
+from leap.soledad.client._document import BlobDoc
+
+
+class BlobPrioritiesTests(unittest.TestCase):
+
+    def setUp(self):
+        self.cleartext = BytesIO('patriarchy is opression')
+        self.secret = 'A' * 96
+        self.manager = BlobManager(
+            self.tempdir, '',
+            'A' * 32, self.secret,
+            uuid4().hex, 'token', None)
+        self.addCleanup(self.manager.close)
+
+    @defer.inlineCallbacks
+    @pytest.mark.usefixtures("method_tmpdir")
+    def test_get_sets_default_priority(self):
+        self.manager._download_and_decrypt = Mock(return_value=None)
+        missing_blob_id = uuid4().hex
+        result = yield self.manager.get(missing_blob_id)
+        self.assertIsNone(result)
+        priority = yield self.manager.get_priority(missing_blob_id)
+        self.assertEqual(Priority.DEFAULT, priority)
+
+    @defer.inlineCallbacks
+    @pytest.mark.usefixtures("method_tmpdir")
+    def test_get_sets_priority(self):
+        self.manager._download_and_decrypt = Mock(return_value=None)
+        missing_blob_id = uuid4().hex
+        urgent = Priority.URGENT
+        result = yield self.manager.get(missing_blob_id, priority=urgent)
+        self.assertIsNone(result)
+        priority = yield self.manager.get_priority(missing_blob_id)
+        self.assertEqual(urgent, priority)
+
+    @defer.inlineCallbacks
+    @pytest.mark.usefixtures("method_tmpdir")
+    def test_put_sets_default_priority(self):
+        upload_failure = defer.fail(Exception())
+        self.manager._encrypt_and_upload = Mock(return_value=upload_failure)
+        content, blob_id = "Blob content", uuid4().hex
+        doc1 = BlobDoc(BytesIO(content), blob_id)
+        with pytest.raises(Exception):
+            yield self.manager.put(doc1, len(content))
+        priority = yield self.manager.get_priority(blob_id)
+        self.assertEqual(Priority.DEFAULT, priority)
+
+    @defer.inlineCallbacks
+    @pytest.mark.usefixtures("method_tmpdir")
+    def test_put_sets_priority(self):
+        upload_failure = defer.fail(Exception())
+        self.manager._encrypt_and_upload = Mock(return_value=upload_failure)
+        content, blob_id = "Blob content", uuid4().hex
+        doc1 = BlobDoc(BytesIO(content), blob_id)
+        urgent = Priority.URGENT
+        with pytest.raises(Exception):
+            yield self.manager.put(doc1, len(content), priority=urgent)
+        priority = yield self.manager.get_priority(blob_id)
+        self.assertEqual(urgent, priority)
+
+    @defer.inlineCallbacks
+    @pytest.mark.usefixtures("method_tmpdir")
+    def test_set_priority_sets_priority(self):
+        self.manager._download_and_decrypt = Mock(return_value=None)
+        missing_blob_id = uuid4().hex
+        result = yield self.manager.get(missing_blob_id)
+        self.assertIsNone(result)
+        urgent = Priority.URGENT
+        yield self.manager.set_priority(missing_blob_id, urgent)
+        priority = yield self.manager.get_priority(missing_blob_id)
+        self.assertEqual(urgent, priority)
+
+    @defer.inlineCallbacks
+    @pytest.mark.usefixtures("method_tmpdir")
+    def test_local_list_status_orders_by_priority(self):
+        self.manager._download_and_decrypt = Mock(return_value=None)
+
+        def _get(priority):
+            missing_blob_id = uuid4().hex
+            d = self.manager.get(missing_blob_id, priority=priority)
+            d.addCallback(lambda _: missing_blob_id)
+            return d
+
+        # get some blobs in arbitrary order
+        low = yield _get(Priority.LOW)
+        high = yield _get(Priority.HIGH)
+        medium = yield _get(Priority.MEDIUM)
+        urgent = yield _get(Priority.URGENT)
+
+        # make sure they are ordered by priority
+        status = SyncStatus.PENDING_DOWNLOAD
+        pending = yield self.manager.local_list_status(status)
+        self.assertEqual([urgent, high, medium, low], pending)
+
+    @defer.inlineCallbacks
+    @pytest.mark.usefixtures("method_tmpdir")
+    def test_refresh_sync_status_from_server_saves_default_priorities(self):
+        remote_ids = [uuid4().hex for _ in range(10)]
+        self.manager.remote_list = Mock(return_value=defer.succeed(remote_ids))
+        yield self.manager.refresh_sync_status_from_server()
+        for blob_id in remote_ids:
+            priority = yield self.manager.get_priority(blob_id)
+            self.assertEquals(Priority.DEFAULT, priority)
+
+    @defer.inlineCallbacks
+    @pytest.mark.usefixtures("method_tmpdir")
+    def test_fetch_missing_fetches_with_priority(self):
+
+        # pretend we have some pending downloads
+        deferreds = [
+            self.manager.local.update_sync_status(
+                'low', SyncStatus.PENDING_DOWNLOAD,
+                priority=Priority.LOW),
+            self.manager.local.update_sync_status(
+                'high', SyncStatus.PENDING_DOWNLOAD,
+                priority=Priority.HIGH),
+            self.manager.local.update_sync_status(
+                'medium', SyncStatus.PENDING_DOWNLOAD,
+                priority=Priority.MEDIUM),
+            self.manager.local.update_sync_status(
+                'urgent', SyncStatus.PENDING_DOWNLOAD,
+                priority=Priority.URGENT),
+        ]
+        yield defer.gatherResults(deferreds)
+
+        # make sure download "succeeds" so fetching works
+        content = 'vegan muffin'
+        fd = BytesIO(content)
+        size = len(content)
+        self.manager._download_and_decrypt = Mock(return_value=(fd, size))
+        self.manager.concurrent_transfers_limit = 1
+
+        # this is the operation we are interested in
+        yield self.manager.fetch_missing()
+
+        # retrieve the order in which blob transfers were made
+        calls = self.manager._download_and_decrypt.mock_calls
+        order = map(lambda c: c[1][0], calls)
+        self.assertEqual(['urgent', 'high', 'medium', 'low'], order)
+
+    @defer.inlineCallbacks
+    @pytest.mark.usefixtures("method_tmpdir")
+    def test_send_missing_sends_with_priority(self):
+
+        # pretend we have some pending uploads
+        _send = self.manager._send
+        self.manager._send = Mock(return_value=None)
+        content = "vegan cake"
+        length = len(content)
+        deferreds = [
+            self.manager.put(
+                BlobDoc(BytesIO(content), 'low'), length,
+                priority=Priority.LOW),
+            self.manager.put(
+                BlobDoc(BytesIO(content), 'high'), length,
+                priority=Priority.HIGH),
+            self.manager.put(
+                BlobDoc(BytesIO(content), 'medium'), length,
+                priority=Priority.MEDIUM),
+            self.manager.put(
+                BlobDoc(BytesIO(content), 'urgent'), length,
+                priority=Priority.URGENT),
+        ]
+        yield defer.gatherResults(deferreds)
+
+        # make sure upload "succeeds" so sending works
+        self.manager._send = _send
+        self.manager._encrypt_and_upload = Mock(return_value=None)
+
+        # this is the operation we are interested in
+        self.manager.concurrent_transfers_limit = 1
+        yield self.manager.send_missing()
+
+        # retrieve the order in which blob transfers were made
+        calls = self.manager._encrypt_and_upload.mock_calls
+        order = map(lambda c: c[1][0], calls)
+        self.assertEqual(['urgent', 'high', 'medium', 'low'], order)
-- 
cgit v1.2.3