Skip to content

Commit

Permalink
final tests for queue, fixes for message sync
Browse files Browse the repository at this point in the history
  • Loading branch information
heartsucker committed May 28, 2019
1 parent 434954c commit 270438e
Show file tree
Hide file tree
Showing 2 changed files with 190 additions and 6 deletions.
94 changes: 94 additions & 0 deletions tests/test_message_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,53 @@ def mock_decrypt_submission_or_reply(filepath, plaintext_filename, is_doc):
assert message.is_decrypted is True


def test_MessageSync_run_decrypt_only(mocker, session, source, session_maker, homedir):
"""
Test when a message successfully downloads and decrypts.
Using the `homedir` fixture to get a GPG keyring.
"""
message = factory.Message(source=source['source'],
is_downloaded=True,
is_decrypted=False,
content=None)
session.add(message)
session.commit()

expected_content = 'foo'

# don't create the signal
mocker.patch('securedrop_client.message_sync.pyqtSignal')

def mock_decrypt_submission_or_reply(filepath, plaintext_filename, is_doc):
with open(plaintext_filename, 'w') as f:
f.write(expected_content)

gpg = GpgHelper(homedir, session_maker, is_qubes=False)
mocker.patch.object(
gpg,
'decrypt_submission_or_reply',
side_effect=mock_decrypt_submission_or_reply,
)

api = mocker.MagicMock(session=session)
ms = MessageSync(api, gpg, session_maker)
ms.api.download_submission = mocker.MagicMock(return_value=(1234, "/home/user/downloads/foo"))
mock_fetch = mocker.patch.object(ms, 'fetch_the_thing')

mock_message_ready = mocker.patch.object(ms, 'message_ready')

# check that it runs without raising exceptions
ms.run(False)

mock_message_ready.emit.assert_called_once_with(message.uuid, expected_content)

session.refresh(message)
assert message.content == expected_content
assert message.is_downloaded is True
assert message.is_decrypted is True
assert not mock_fetch.called


def test_MessageSync_run_decryption_error(mocker, session, source, session_maker, homedir):
"""
Test when a message successfully downloads, but does not successfully decrypt.
Expand Down Expand Up @@ -202,6 +249,53 @@ def mock_decrypt_submission_or_reply(filepath, plaintext_filename, is_doc):
assert reply.is_decrypted is True


def test_ReplySync_run_decrypt_only(mocker, session, source, session_maker, homedir):
"""
Test when a message successfully downloads and decrypts.
Using the `homedir` fixture to get a GPG keyring.
"""
reply = factory.Reply(source=source['source'],
is_downloaded=True,
is_decrypted=False,
content=None)
session.add(reply)
session.commit()

expected_content = 'foo'

# don't create the signal
mocker.patch('securedrop_client.message_sync.pyqtSignal')

def mock_decrypt_submission_or_reply(filepath, plaintext_filename, is_doc):
with open(plaintext_filename, 'w') as f:
f.write(expected_content)

gpg = GpgHelper(homedir, session_maker, is_qubes=False)
mocker.patch.object(
gpg,
'decrypt_submission_or_reply',
side_effect=mock_decrypt_submission_or_reply,
)

api = mocker.MagicMock(session=session)
rs = ReplySync(api, gpg, session_maker)
rs.api.download_submission = mocker.MagicMock(return_value=(1234, "/home/user/downloads/foo"))
mock_fetch = mocker.patch.object(rs, 'fetch_the_thing')

mock_message_ready = mocker.patch.object(rs, 'reply_ready')

# check that it runs without raising exceptions
rs.run(False)

mock_message_ready.emit.assert_called_once_with(reply.uuid, expected_content)

session.refresh(reply)
assert reply.content == expected_content
assert reply.is_downloaded is True
assert reply.is_decrypted is True
assert not mock_fetch.called


def test_ReplySync_run_decryption_error(mocker, session, source, session_maker, homedir):
"""
Test when a reply successfully downloads, but does not successfully decrypt.
Expand Down
102 changes: 96 additions & 6 deletions tests/test_queue.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@
from typing import Tuple

from securedrop_client import db
from securedrop_client.crypto import GpgHelper
from securedrop_client.crypto import GpgHelper, CryptoError
from securedrop_client.queue import ApiInaccessibleError, ApiJob, RunnableQueue, \
DownloadSubmissionJob
DownloadSubmissionJob, ApiJobQueue


def test_ApiInaccessibleError_init():
Expand Down Expand Up @@ -219,6 +219,7 @@ def test_DownloadSubmissionJob_happy_path_no_etag(mocker, homedir, session, sess
session.commit()

gpg = GpgHelper(homedir, session_maker, is_qubes=False)
mock_decrypt = mocker.patch.object(gpg, 'decrypt_submission_or_reply')

def fake_download(sdk_obj: sdclientapi.Submission) -> Tuple[str, str]:
'''
Expand All @@ -239,7 +240,6 @@ def fake_download(sdk_obj: sdclientapi.Submission) -> Tuple[str, str]:
gpg,
)

mock_decrypt = mocker.patch.object(job, '_decrypt_file')
mock_logger = mocker.patch('securedrop_client.queue.logger')

job.call_api(api_client, session)
Expand All @@ -259,6 +259,7 @@ def test_DownloadSubmissionJob_happy_path_sha256_etag(mocker, homedir, session,
session.commit()

gpg = GpgHelper(homedir, session_maker, is_qubes=False)
mock_decrypt = mocker.patch.object(gpg, 'decrypt_submission_or_reply')

def fake_download(sdk_obj: sdclientapi.Submission) -> Tuple[str, str]:
'''
Expand All @@ -282,8 +283,6 @@ def fake_download(sdk_obj: sdclientapi.Submission) -> Tuple[str, str]:
gpg,
)

mock_decrypt = mocker.patch.object(job, '_decrypt_file')

job.call_api(api_client, session)

# ensure mocks aren't stale
Expand Down Expand Up @@ -354,7 +353,7 @@ def fake_download(sdk_obj: sdclientapi.Submission) -> Tuple[str, str]:
gpg,
)

mock_decrypt = mocker.patch.object(job, '_decrypt_file')
mock_decrypt = mocker.patch('securedrop_client.crypto.GpgHelper.decrypt_submission_or_reply')
mock_logger = mocker.patch('securedrop_client.queue.logger')

job.call_api(api_client, session)
Expand All @@ -364,3 +363,94 @@ def fake_download(sdk_obj: sdclientapi.Submission) -> Tuple[str, str]:

# ensure mocks aren't stale
assert mock_decrypt.called


def test_DownloadSubmissionJob_decryption_error(mocker, homedir, session, session_maker):
source = factory.Source()
file_ = factory.File(source=source)
session.add(source)
session.add(file_)
session.commit()

gpg = GpgHelper(homedir, session_maker, is_qubes=False)
mock_decrypt = mocker.patch.object(gpg, 'decrypt_submission_or_reply',
side_effect=CryptoError)

def fake_download(sdk_obj: sdclientapi.Submission) -> Tuple[str, str]:
'''
:return: (etag, path_to_dl)
'''
full_path = os.path.join(homedir, 'somepath')
with open(full_path, 'wb') as f:
f.write(b'wat')

# sha256 of b'wat'
return ('sha256:f00a787f7492a95e165b470702f4fe9373583fbdc025b2c8bdf0262cc48fcff4',
full_path)

api_client = mocker.MagicMock()
api_client.download_submission = fake_download

job = DownloadSubmissionJob(
db.File,
file_.uuid,
homedir,
gpg,
)

mock_logger = mocker.patch('securedrop_client.queue.logger')

with pytest.raises(CryptoError):
job.call_api(api_client, session)

log_msg = mock_logger.debug.call_args_list[0][0][0]
assert log_msg.startswith('Failed to decrypt file')

# ensure mocks aren't stale
assert mock_decrypt.called


def test_ApiJobQueue_enqueue(mocker):
mock_client = mocker.MagicMock()
mock_session_maker = mocker.MagicMock()

job_queue = ApiJobQueue(mock_client, mock_session_maker)
mock_download_queue = mocker.patch.object(job_queue, 'download_queue')
mock_main_queue = mocker.patch.object(job_queue, 'main_queue')

dl_job = DownloadSubmissionJob(db.File, 'mock', 'mock', 'mock')
job_queue.enqueue(dl_job)

mock_download_queue.queue.put_nowait.assert_called_once_with(dl_job)
assert not mock_main_queue.queue.put_nowait.called

# reset for next test
mock_download_queue.reset_mock()
mock_main_queue.reset_mock()

dummy_job = dummy_job_factory(mocker, 'mock')()
job_queue.enqueue(dummy_job)

mock_main_queue.queue.put_nowait.assert_called_once_with(dummy_job)
assert not mock_download_queue.queue.put_nowait.called


def test_ApiJobQueue_start_queues(mocker):
mock_api = mocker.MagicMock()
mock_client = mocker.MagicMock()
mock_session_maker = mocker.MagicMock()

job_queue = ApiJobQueue(mock_client, mock_session_maker)

mock_main_queue = mocker.patch.object(job_queue, 'main_queue')
mock_download_queue = mocker.patch.object(job_queue, 'download_queue')
mock_main_thread = mocker.patch.object(job_queue, 'main_thread')
mock_download_thread = mocker.patch.object(job_queue, 'download_thread')

job_queue.start_queues(mock_api)

assert mock_main_queue.api_client == mock_api
assert mock_download_queue.api_client == mock_api

mock_main_thread.start.assert_called_once_with()
mock_download_thread.start.assert_called_once_with()

0 comments on commit 270438e

Please sign in to comment.