Skip to content

Commit

Permalink
test: updates for export logic moving into its own thread
Browse files Browse the repository at this point in the history
  • Loading branch information
redshiftzero committed Oct 15, 2019
1 parent 39a71e1 commit a83e2c9
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 91 deletions.
117 changes: 41 additions & 76 deletions tests/gui/test_widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -1432,14 +1432,12 @@ def test_FileWidget__on_export_clicked(mocker, session, source):

fw = FileWidget(file.uuid, controller, mocker.MagicMock())
fw.update = mocker.MagicMock()
_request_pass = mocker.patch('securedrop_client.gui.widgets.ExportDialog._request_passphrase')
mocker.patch('securedrop_client.gui.widgets.QDialog.exec')
controller.run_export_preflight_checks = mocker.MagicMock()

fw._on_export_clicked()

controller.run_export_preflight_checks.assert_called_once_with()
_request_pass.assert_called_once_with()


def test_ExportDialog_export(mocker):
Expand All @@ -1453,12 +1451,11 @@ def test_ExportDialog_export(mocker):
export_dialog.export()

controller.run_export_preflight_checks.assert_called_with()
export_dialog._request_passphrase.assert_called_with()


def test_ExportDialog_export_request_to_insert_usb_device_on_CALLED_PROCESS_ERROR(mocker):
def test_ExportDialog_pre_flight_request_to_insert_usb_device_on_CALLED_PROCESS_ERROR(mocker):
"""
Ensure request to insert USB device on CALLED_PROCESS_ERROR.
Ensure request to insert USB device on CALLED_PROCESS_ERROR during pre-flight.
"""
controller = mocker.MagicMock()
called_process_error = ExportError(ExportStatus.CALLED_PROCESS_ERROR.value)
Expand All @@ -1468,112 +1465,58 @@ def test_ExportDialog_export_request_to_insert_usb_device_on_CALLED_PROCESS_ERRO
export_dialog._request_to_insert_usb_device = mocker.MagicMock()
export_dialog._update = mocker.MagicMock()

export_dialog.export()
export_dialog._update_preflight(called_process_error.status)

export_dialog._request_passphrase.assert_not_called()
export_dialog._request_to_insert_usb_device.assert_called_once_with()
export_dialog._update.assert_not_called()


def test_ExportDialog_export_request_to_insert_usb_device_on_USB_NOT_CONNECTED(mocker):
"""
Ensure request to insert USB device on USB_NOT_CONNECTED.
"""
controller = mocker.MagicMock()
usb_not_connected_error = ExportError(ExportStatus.USB_NOT_CONNECTED.value)
controller.run_export_preflight_checks = mocker.MagicMock(side_effect=usb_not_connected_error)
export_dialog = ExportDialog(controller, 'mock_uuid')
export_dialog._request_passphrase = mocker.MagicMock()
export_dialog._update = mocker.MagicMock()

export_dialog.export()

export_dialog._request_passphrase.assert_not_called()
export_dialog._update.assert_called_once_with('USB_NOT_CONNECTED')


def test_ExportDialog_export_updates_on_ExportError(mocker):
def test_ExportDialog_export_request_to_insert_usb_device_on_CALLED_PROCESS_ERROR(mocker):
"""
Ensure update is run for ExportError that is not USB_NOT_CONNECTED or CALLED_PROCESS_ERROR.
Ensure request to insert USB device on CALLED_PROCESS_ERROR during export.
"""
controller = mocker.MagicMock()
controller.run_export_preflight_checks = mocker.MagicMock(side_effect=ExportError('mock'))
called_process_error = ExportError(ExportStatus.CALLED_PROCESS_ERROR.value)
controller.run_export_preflight_checks = mocker.MagicMock(side_effect=called_process_error)
export_dialog = ExportDialog(controller, 'mock_uuid')
export_dialog._request_passphrase = mocker.MagicMock()
export_dialog._request_to_insert_usb_device = mocker.MagicMock()
export_dialog._update = mocker.MagicMock()

export_dialog.export()
export_dialog._update_usb_export(called_process_error.status)

export_dialog._request_passphrase.assert_not_called()
export_dialog._update.assert_called_once_with('mock')
export_dialog._request_to_insert_usb_device.assert_not_called()
export_dialog._update.assert_called_once_with(called_process_error.status)


def test_ExportDialog__on_retry_export_button_clicked(mocker):
"""
Ensure happy path runs preflight checks and requests passphrase.
Ensure happy path runs preflight checks.
"""
controller = mocker.MagicMock()
export_dialog = ExportDialog(controller, 'mock_uuid')
export_dialog._request_passphrase = mocker.MagicMock()

export_dialog._on_retry_export_button_clicked()

controller.run_export_preflight_checks.assert_called_with()
export_dialog._request_passphrase.assert_called_with()


def test_ExportDialog__on_retry_export_button_clicked_USB_NOT_CONNECTED(mocker):
def test_ExportDialog__update_export_button_clicked_USB_NOT_CONNECTED(mocker):
"""
Ensure request to insert USB device on USB_NOT_CONNECTED.
"""
controller = mocker.MagicMock()
usb_not_connected_error = ExportError(ExportStatus.USB_NOT_CONNECTED.value)
controller.run_export_preflight_checks = mocker.MagicMock(side_effect=usb_not_connected_error)
export_dialog = ExportDialog(controller, 'mock_uuid')
export_dialog._request_passphrase = mocker.MagicMock()
export_dialog._update = mocker.MagicMock()

export_dialog._on_retry_export_button_clicked()

export_dialog._request_passphrase.assert_not_called()
export_dialog._update.assert_called_once_with('USB_NOT_CONNECTED')


def test_ExportDialog__on_retry_export_button_clicked_CALLED_PROCESS_ERROR(mocker):
"""
Ensure update is run on CALLED_PROCESS_ERROR.
"""
controller = mocker.MagicMock()
called_process_error = ExportError(ExportStatus.CALLED_PROCESS_ERROR.value)
controller.run_export_preflight_checks = mocker.MagicMock(side_effect=called_process_error)
export_dialog = ExportDialog(controller, 'mock_uuid')
export_dialog._request_passphrase = mocker.MagicMock()
export_dialog._request_to_insert_usb_device = mocker.MagicMock()
export_dialog._update = mocker.MagicMock()

export_dialog._on_retry_export_button_clicked()
export_dialog._update(usb_not_connected_error.status)

export_dialog._request_passphrase.assert_not_called()
export_dialog._request_to_insert_usb_device.assert_not_called()
export_dialog._update.assert_called_once_with('CALLED_PROCESS_ERROR')


def test_ExportDialog__on_retry_export_button_clicked_updates_on_ExportError(mocker):
"""
Ensure update is run for ExportError that is not USB_NOT_CONNECTED.
"""
controller = mocker.MagicMock()
controller.run_export_preflight_checks = mocker.MagicMock(side_effect=ExportError('mock'))
export_dialog = ExportDialog(controller, 'mock_uuid')
export_dialog._request_passphrase = mocker.MagicMock()
export_dialog._request_to_insert_usb_device = mocker.MagicMock()
export_dialog._update = mocker.MagicMock()

export_dialog._on_retry_export_button_clicked()

export_dialog._request_passphrase.assert_not_called()
export_dialog._request_to_insert_usb_device.assert_not_called()
export_dialog._update.assert_called_once_with('mock')
export_dialog._request_to_insert_usb_device.assert_called_once_with()


def test_ExportDialog__request_to_insert_usb_device(mocker):
Expand Down Expand Up @@ -1620,6 +1563,19 @@ def test_ExportDialog__request_passphrase_more_than_once(mocker):
assert not export_dialog.passphrase_error_message.isHidden()


def test_ExportDialog__on_export_success_closes_window(mocker):
"""
Ensure successful export results in the export dialog window closing.
"""
controller = mocker.MagicMock()
export_dialog = ExportDialog(controller, 'mock_uuid')
export_dialog.close = mocker.MagicMock()

export_dialog._on_export_success()

export_dialog.close.assert_called_once_with()


def test_ExportDialog__on_unlock_disk_clicked(mocker):
"""
Ensure export of file begins once the passphrase is retrieved from the uesr.
Expand All @@ -1642,16 +1598,25 @@ def test_ExportDialog__on_unlock_disk_clicked_asks_for_passphrase_again_on_error
"""
controller = mocker.MagicMock()
bad_password_export_error = ExportError(ExportStatus.BAD_PASSPHRASE.value)
controller.export_file_to_usb_drive = mocker.MagicMock(side_effect=bad_password_export_error)
export_dialog = ExportDialog(controller, 'mock_uuid')
export_dialog._request_passphrase = mocker.MagicMock()
export_dialog.passphrase_field.text = mocker.MagicMock(return_value='mock_passphrase')

export_dialog._on_unlock_disk_clicked()
export_dialog._update_usb_export(bad_password_export_error.status)

export_dialog._request_passphrase.assert_called_with(True)


def test_ExportDialog__update_preflight_non_called_process_error(mocker):
"""
Ensure generic errors are passed through to _update
"""
export_dialog = ExportDialog(mocker.MagicMock(), 'mock_uuid')
export_dialog.generic_error = mocker.MagicMock()
export_dialog._update_preflight(ExportStatus.UNEXPECTED_RETURN_STATUS.value)
export_dialog.generic_error.show.assert_called_once_with()


def test_ExportDialog__update_after_USB_NOT_CONNECTED(mocker):
"""
Ensure USB_NOT_CONNECTED results in asking the user connect their USB device.
Expand All @@ -1674,14 +1639,14 @@ def test_ExportDialog__update_after_DISK_ENCRYPTION_NOT_SUPPORTED_ERROR(mocker):

def test_ExportDialog__update_after_CALLED_PROCESS_ERROR_ERROR(mocker):
"""
Ensure CALLED_PROCESS_ERROR shows generic 'contant admin' error with correct
Ensure CALLED_PROCESS_ERROR shows generic 'contact admin' error with correct
error status code.
"""
export_dialog = ExportDialog(mocker.MagicMock(), 'mock_uuid')
export_dialog._request_to_insert_usb_device = mocker.MagicMock()
export_dialog._request_passphrase = mocker.MagicMock()

export_dialog._update(ExportStatus.CALLED_PROCESS_ERROR.value)
export_dialog._update_usb_export(ExportStatus.CALLED_PROCESS_ERROR.value)

assert not export_dialog.generic_error.isHidden()
assert export_dialog.error_status_code.text() == 'CALLED_PROCESS_ERROR'
Expand Down
52 changes: 50 additions & 2 deletions tests/test_export.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,35 +9,83 @@
def test_send_file_to_usb_device(mocker):
'''
Ensure TemporaryDirectory is used when creating and sending the archive containing the export
file.
file and that the success signal is emitted.
'''
mock_temp_dir = mocker.MagicMock()
mock_temp_dir.__enter__ = mocker.MagicMock(return_value='mock_temp_dir')
mocker.patch('securedrop_client.export.TemporaryDirectory', return_value=mock_temp_dir)
export = Export()
export.export_usb_call_success = mocker.MagicMock()
export.export_usb_call_success.emit = mocker.MagicMock()
_run_disk_export = mocker.patch.object(export, '_run_disk_export')

export.send_file_to_usb_device(['mock_filepath'], 'mock passphrase')

_run_disk_export.assert_called_once_with('mock_temp_dir', ['mock_filepath'], 'mock passphrase')
export.export_usb_call_success.emit.assert_called_once_with(['mock_filepath'])


def test_send_file_to_usb_device_error(mocker):
'''
Ensure TemporaryDirectory is used when creating and sending the archive containing the export
file and that the failure signal is emitted.
'''
mock_temp_dir = mocker.MagicMock()
mock_temp_dir.__enter__ = mocker.MagicMock(return_value='mock_temp_dir')
mocker.patch('securedrop_client.export.TemporaryDirectory', return_value=mock_temp_dir)
export = Export()
export.export_usb_call_failure = mocker.MagicMock()
export.export_usb_call_failure.emit = mocker.MagicMock()
error = ExportError('bang')
_run_disk_export = mocker.patch.object(export, '_run_disk_export', side_effect=error)

export.send_file_to_usb_device(['mock_filepath'], 'mock passphrase')

_run_disk_export.assert_called_once_with('mock_temp_dir', ['mock_filepath'], 'mock passphrase')
export.export_usb_call_failure.emit.assert_called_once_with(error.status)


def test_run_preflight_checks(mocker):
'''
Ensure TemporaryDirectory is used when creating and sending the archives during the preflight
checks.
checks and that the success signal is emitted by Export.
'''
mock_temp_dir = mocker.MagicMock()
mock_temp_dir.__enter__ = mocker.MagicMock(return_value='mock_temp_dir')
mocker.patch('securedrop_client.export.TemporaryDirectory', return_value=mock_temp_dir)
export = Export()
export.preflight_check_call_success = mocker.MagicMock()
export.preflight_check_call_success.emit = mocker.MagicMock()
_run_usb_export = mocker.patch.object(export, '_run_usb_test')
_run_disk_export = mocker.patch.object(export, '_run_disk_test')

export.run_preflight_checks()

_run_usb_export.assert_called_once_with('mock_temp_dir')
_run_disk_export.assert_called_once_with('mock_temp_dir')
export.preflight_check_call_success.emit.assert_called_once_with('success')


def test_run_preflight_checks_error(mocker):
'''
Ensure TemporaryDirectory is used when creating and sending the archives during the preflight
checks and that the failure signal is emitted by Export.
'''
mock_temp_dir = mocker.MagicMock()
mock_temp_dir.__enter__ = mocker.MagicMock(return_value='mock_temp_dir')
mocker.patch('securedrop_client.export.TemporaryDirectory', return_value=mock_temp_dir)
export = Export()
export.preflight_check_call_failure = mocker.MagicMock()
export.preflight_check_call_failure.emit = mocker.MagicMock()
error = ExportError('bang!')
_run_usb_export = mocker.patch.object(export, '_run_usb_test')
_run_disk_export = mocker.patch.object(export, '_run_disk_test', side_effect=error)

export.run_preflight_checks()

_run_usb_export.assert_called_once_with('mock_temp_dir')
_run_disk_export.assert_called_once_with('mock_temp_dir')
export.preflight_check_call_failure.emit.assert_called_once_with(error.status)


def test__run_disk_export(mocker):
Expand Down
28 changes: 15 additions & 13 deletions tests/test_logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,9 @@ def test_Controller_setup(homedir, config, mocker, session_maker):
Ensure the application is set up with the following default state:
Using the `config` fixture to ensure the config is written to disk.
"""
mocker.patch('securedrop_client.logic.QThread')
co = Controller('http://localhost', mocker.MagicMock(), session_maker, homedir)
co.export.moveToThread = mocker.MagicMock()
co.update_sources = mocker.MagicMock()

co.setup()
Expand Down Expand Up @@ -1417,50 +1419,50 @@ def test_Controller_call_update_star_success(homedir, config, mocker, session_ma


def test_Controller_run_export_preflight_checks(homedir, mocker):
debug_logger = mocker.patch('securedrop_client.logic.logger.debug')
co = Controller('http://localhost', mocker.MagicMock(), mocker.MagicMock(), homedir)
co.export = mocker.MagicMock()
co.export.begin_preflight_check = mocker.MagicMock()
co.export.begin_preflight_check.emit = mocker.MagicMock()

co.run_export_preflight_checks()

co.export.run_preflight_checks.assert_called_once_with()
debug_logger.assert_called_once_with('Running export preflight checks')
co.export.begin_usb_export.emit.call_count == 1


def test_Controller_run_export_preflight_checks_not_qubes(homedir, mocker):
debug_logger = mocker.patch('securedrop_client.logic.logger.debug')
co = Controller('http://localhost', mocker.MagicMock(), mocker.MagicMock(), homedir)
co.qubes = False
co.export = mocker.MagicMock()
co.export.begin_preflight_check = mocker.MagicMock()
co.export.begin_preflight_check.emit = mocker.MagicMock()

co.run_export_preflight_checks()

co.export.run_preflight_checks.assert_not_called()
debug_logger.assert_called_once_with('Running export preflight checks')
co.export.begin_usb_export.emit.call_count == 0


def test_Controller_export_file_to_usb_drive(homedir, mocker, session):
debug_logger = mocker.patch('securedrop_client.logic.logger.debug')
co = Controller('http://localhost', mocker.MagicMock(), mocker.MagicMock(), homedir)
co.export = mocker.MagicMock()
co.export.begin_usb_export = mocker.MagicMock()
co.export.begin_usb_export.emit = mocker.MagicMock()
file = factory.File(source=factory.Source(), original_filename='mock_filename')
session.add(file)
session.commit()
mocker.patch('securedrop_client.logic.Controller.get_file', return_value=file)

co.export_file_to_usb_drive(file.uuid, 'mock passphrase')

co.export.send_file_to_usb_device.assert_called_once_with(
[os.path.join(co.data_dir, 'mock_filename')], 'mock passphrase')
debug_logger.call_args_list[0][0][0] == 'Exporting {}'.format(file.original_filename)
debug_logger.call_args_list[1][0][0] == 'Export successful'
# Signal to begin the USB export should be emitted
co.export.begin_usb_export.emit.call_count == 1


def test_Controller_export_file_to_usb_drive_not_qubes(homedir, mocker, session):
debug_logger = mocker.patch('securedrop_client.logic.logger.debug')
co = Controller('http://localhost', mocker.MagicMock(), mocker.MagicMock(), homedir)
co.qubes = False
co.export = mocker.MagicMock()
co.export.begin_usb_export = mocker.MagicMock()
co.export.begin_usb_export.emit = mocker.MagicMock()
file = factory.File(source=factory.Source(), original_filename='mock_filename')
session.add(file)
session.commit()
Expand All @@ -1469,4 +1471,4 @@ def test_Controller_export_file_to_usb_drive_not_qubes(homedir, mocker, session)
co.export_file_to_usb_drive(file.uuid, 'mock passphrase')

co.export.send_file_to_usb_device.assert_not_called()
debug_logger.call_args_list[0][0][0] == 'Exporting {}'.format(file.original_filename)
co.export.begin_usb_export.emit.call_count == 0

0 comments on commit a83e2c9

Please sign in to comment.