Skip to content

Commit

Permalink
update and add tests for new background sync
Browse files Browse the repository at this point in the history
  • Loading branch information
Allie Crevier committed Jan 28, 2020
1 parent 0debf6e commit 9e7d342
Show file tree
Hide file tree
Showing 4 changed files with 251 additions and 105 deletions.
104 changes: 1 addition & 103 deletions tests/api_jobs/test_downloads.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from sdclientapi import Submission as SdkSubmission

from securedrop_client.api_jobs.downloads import DownloadJob, FileDownloadJob, MessageDownloadJob, \
ReplyDownloadJob, DownloadChecksumMismatchException, MetadataSyncJob
ReplyDownloadJob, DownloadChecksumMismatchException
from securedrop_client.crypto import GpgHelper, CryptoError
from tests import factory

Expand All @@ -21,108 +21,6 @@ def patch_decrypt(mocker, homedir, gpghelper, filename):
return mock_decrypt


def test_MetadataSyncJob_success(mocker, homedir, session, session_maker):
gpg = GpgHelper(homedir, session_maker, is_qubes=False)
job = MetadataSyncJob(homedir, gpg)

mock_source = mocker.MagicMock()
mock_source.uuid = 'bar'
mock_source.key = {
'type': 'PGP',
'public': PUB_KEY,
'fingerprint': '123456ABC',
}

mock_key_import = mocker.patch.object(job.gpg, 'import_key')
mock_get_remote_data = mocker.patch(
'securedrop_client.api_jobs.downloads.get_remote_data',
return_value=([mock_source], 'submissions', 'replies'))

api_client = mocker.MagicMock()
api_client.default_request_timeout = mocker.MagicMock()
api_client.default_request_timeout = mocker.MagicMock()

mocker.patch(
'securedrop_client.api_jobs.downloads.update_local_storage',
return_value=([mock_source], 'submissions', 'replies'))

job.call_api(api_client, session)

assert mock_key_import.call_args[0][0] == mock_source.uuid
assert mock_key_import.call_args[0][1] == mock_source.key['public']
assert mock_key_import.call_args[0][2] == mock_source.key['fingerprint']
assert mock_get_remote_data.call_count == 1


def test_MetadataSyncJob_success_with_key_import_fail(mocker, homedir, session, session_maker):
"""
Check that we can gracefully handle a key import failure.
"""
gpg = GpgHelper(homedir, session_maker, is_qubes=False)
job = MetadataSyncJob(homedir, gpg)

mock_source = mocker.MagicMock()
mock_source.uuid = 'bar'
mock_source.key = {
'type': 'PGP',
'public': PUB_KEY,
'fingerprint': '123456ABC',
}

mock_key_import = mocker.patch.object(job.gpg, 'import_key',
side_effect=CryptoError)
mock_get_remote_data = mocker.patch(
'securedrop_client.api_jobs.downloads.get_remote_data',
return_value=([mock_source], 'submissions', 'replies'))

api_client = mocker.MagicMock()
api_client.default_request_timeout = mocker.MagicMock()

mocker.patch(
'securedrop_client.api_jobs.downloads.update_local_storage',
return_value=([mock_source], 'submissions', 'replies'))

job.call_api(api_client, session)

assert mock_key_import.call_args[0][0] == mock_source.uuid
assert mock_key_import.call_args[0][1] == mock_source.key['public']
assert mock_key_import.call_args[0][2] == mock_source.key['fingerprint']
assert mock_get_remote_data.call_count == 1


def test_MetadataSyncJob_success_with_missing_key(mocker, homedir, session, session_maker):
"""
Check that we can gracefully handle missing source keys.
"""
gpg = GpgHelper(homedir, session_maker, is_qubes=False)
job = MetadataSyncJob(homedir, gpg)

mock_source = mocker.MagicMock()
mock_source.uuid = 'bar'
mock_source.key = {
'type': 'PGP',
'pub_key': '',
'fingerprint': ''
}

mock_key_import = mocker.patch.object(job.gpg, 'import_key')
mock_get_remote_data = mocker.patch(
'securedrop_client.api_jobs.downloads.get_remote_data',
return_value=([mock_source], 'submissions', 'replies'))

api_client = mocker.MagicMock()
api_client.default_request_timeout = mocker.MagicMock()

mocker.patch(
'securedrop_client.api_jobs.downloads.update_local_storage',
return_value=([mock_source], 'submissions', 'replies'))

job.call_api(api_client, session)

assert mock_key_import.call_count == 0
assert mock_get_remote_data.call_count == 1


def test_MessageDownloadJob_raises_NotImplementedError(mocker):
job = DownloadJob('mock')

Expand Down
109 changes: 109 additions & 0 deletions tests/api_jobs/test_sync.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import os

from securedrop_client.api_jobs.sync import MetadataSyncJob
from securedrop_client.crypto import GpgHelper, CryptoError

with open(os.path.join(os.path.dirname(__file__), '..', 'files', 'test-key.gpg.pub.asc')) as f:
PUB_KEY = f.read()


def test_MetadataSyncJob_success(mocker, homedir, session, session_maker):
gpg = GpgHelper(homedir, session_maker, is_qubes=False)
job = MetadataSyncJob(homedir, gpg)

mock_source = mocker.MagicMock()
mock_source.uuid = 'bar'
mock_source.key = {
'type': 'PGP',
'public': PUB_KEY,
'fingerprint': '123456ABC',
}

mock_key_import = mocker.patch.object(job.gpg, 'import_key')
mock_get_remote_data = mocker.patch(
'securedrop_client.api_jobs.sync.get_remote_data',
return_value=([mock_source], 'submissions', 'replies'))

api_client = mocker.MagicMock()
api_client.default_request_timeout = mocker.MagicMock()
api_client.default_request_timeout = mocker.MagicMock()

mocker.patch(
'securedrop_client.api_jobs.sync.update_local_storage',
return_value=([mock_source], 'submissions', 'replies'))

job.call_api(api_client, session)

assert mock_key_import.call_args[0][0] == mock_source.uuid
assert mock_key_import.call_args[0][1] == mock_source.key['public']
assert mock_key_import.call_args[0][2] == mock_source.key['fingerprint']
assert mock_get_remote_data.call_count == 1


def test_MetadataSyncJob_success_with_key_import_fail(mocker, homedir, session, session_maker):
"""
Check that we can gracefully handle a key import failure.
"""
gpg = GpgHelper(homedir, session_maker, is_qubes=False)
job = MetadataSyncJob(homedir, gpg)

mock_source = mocker.MagicMock()
mock_source.uuid = 'bar'
mock_source.key = {
'type': 'PGP',
'public': PUB_KEY,
'fingerprint': '123456ABC',
}

mock_key_import = mocker.patch.object(job.gpg, 'import_key',
side_effect=CryptoError)
mock_get_remote_data = mocker.patch(
'securedrop_client.api_jobs.sync.get_remote_data',
return_value=([mock_source], 'submissions', 'replies'))

api_client = mocker.MagicMock()
api_client.default_request_timeout = mocker.MagicMock()

mocker.patch(
'securedrop_client.api_jobs.sync.update_local_storage',
return_value=([mock_source], 'submissions', 'replies'))

job.call_api(api_client, session)

assert mock_key_import.call_args[0][0] == mock_source.uuid
assert mock_key_import.call_args[0][1] == mock_source.key['public']
assert mock_key_import.call_args[0][2] == mock_source.key['fingerprint']
assert mock_get_remote_data.call_count == 1


def test_MetadataSyncJob_success_with_missing_key(mocker, homedir, session, session_maker):
"""
Check that we can gracefully handle missing source keys.
"""
gpg = GpgHelper(homedir, session_maker, is_qubes=False)
job = MetadataSyncJob(homedir, gpg)

mock_source = mocker.MagicMock()
mock_source.uuid = 'bar'
mock_source.key = {
'type': 'PGP',
'pub_key': '',
'fingerprint': ''
}

mock_key_import = mocker.patch.object(job.gpg, 'import_key')
mock_get_remote_data = mocker.patch(
'securedrop_client.api_jobs.sync.get_remote_data',
return_value=([mock_source], 'submissions', 'replies'))

api_client = mocker.MagicMock()
api_client.default_request_timeout = mocker.MagicMock()

mocker.patch(
'securedrop_client.api_jobs.sync.update_local_storage',
return_value=([mock_source], 'submissions', 'replies'))

job.call_api(api_client, session)

assert mock_key_import.call_count == 0
assert mock_get_remote_data.call_count == 1
19 changes: 17 additions & 2 deletions tests/test_logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,10 +175,12 @@ def test_Controller_on_authenticate_failure(homedir, config, mocker, session_mak
mock_gui = mocker.MagicMock()

co = Controller('http://localhost', mock_gui, session_maker, homedir)
co.api_sync.stop = mocker.MagicMock()

result_data = Exception('oh no')
co.on_authenticate_failure(result_data)

co.api_sync.stop.assert_called_once_with()
mock_gui.show_login_error.\
assert_called_once_with(error='There was a problem signing in. Please '
'verify your credentials and try again.')
Expand All @@ -194,7 +196,8 @@ def test_Controller_on_authenticate_success(homedir, config, mocker, session_mak
mock_gui = mocker.MagicMock()
mock_api_job_queue = mocker.patch("securedrop_client.logic.ApiJobQueue")
co = Controller('http://localhost', mock_gui, session_maker, homedir)
co.sync_api = mocker.MagicMock()
# co.api_sync = mocker.MagicMock()
co.api_sync.start = mocker.MagicMock()
co.update_sources = mocker.MagicMock()
co.session.add(user)
co.session.commit()
Expand All @@ -208,7 +211,7 @@ def test_Controller_on_authenticate_success(homedir, config, mocker, session_mak

co.on_authenticate_success(True)

co.sync_api.assert_called_once_with()
co.api_sync.start.assert_called_once_with(co.api)
assert co.is_authenticated
assert mock_api_job_queue.called
co.update_sources.assert_called_once_with()
Expand Down Expand Up @@ -409,6 +412,18 @@ def test_Controller_last_sync_no_file(homedir, config, mocker, session_maker):
assert co.last_sync() is None


def test_Controller_on_sync_started(mocker, homedir):
co = Controller('http://localhost', mocker.MagicMock(), mocker.MagicMock(), homedir)

co.on_sync_started()

sync_events = mocker.patch.object(co, 'sync_events')

co.on_sync_started()

sync_events.emit.assert_called_once_with('syncing')


def test_Controller_on_sync_failure(homedir, config, mocker, session_maker):
"""
If there's no result to syncing, then don't attempt to update local storage
Expand Down
Loading

0 comments on commit 9e7d342

Please sign in to comment.