diff --git a/securedrop_client/api_jobs/updatestar.py b/securedrop_client/api_jobs/updatestar.py index 77bd0a4287..e2473a6861 100644 --- a/securedrop_client/api_jobs/updatestar.py +++ b/securedrop_client/api_jobs/updatestar.py @@ -35,7 +35,7 @@ def call_api(self, api_client: API, session: Session) -> str: return self.source_uuid except (RequestTimeoutError, ServerConnectionError) as e: - error_message = f'Failed to update star on source {self.source_uuid} due to {e}' + error_message = f'Failed to update star on source {self.source_uuid} due to error: {e}' raise UpdateStarJobTimeoutError(error_message, self.source_uuid, self.is_starred) except Exception as e: error_message = f'Failed to update star on source {self.source_uuid} due to {e}' diff --git a/securedrop_client/gui/widgets.py b/securedrop_client/gui/widgets.py index 161dbb9dc2..83fb846cb1 100644 --- a/securedrop_client/gui/widgets.py +++ b/securedrop_client/gui/widgets.py @@ -1055,7 +1055,6 @@ def __init__(self, controller: Controller, source: Source): # Store source self.source_uuid = source.uuid self.source = source - self.source_uuid = source.uuid # Set styles self.setStyleSheet(self.CSS) @@ -1081,7 +1080,7 @@ def __init__(self, controller: Controller, source: Source): gutter_layout = QVBoxLayout(self.gutter) gutter_layout.setContentsMargins(0, 0, 0, 0) gutter_layout.setSpacing(0) - self.star = StarToggleButton(self.controller, self.source) + self.star = StarToggleButton(self.controller, self.source_uuid, source.is_starred) gutter_layout.addWidget(self.star) gutter_layout.addStretch() @@ -1206,12 +1205,12 @@ class StarToggleButton(SvgToggleButton): } ''' - def __init__(self, controller: Controller, source: Source): + def __init__(self, controller: Controller, source_uuid: str, is_starred: bool): super().__init__(on='star_on.svg', off='star_off.svg', svg_size=QSize(16, 16)) self.controller = controller - self.source_uuid = source.uuid - self.is_starred = source.is_starred + self.source_uuid = source_uuid + self.is_starred = is_starred self.pending_count = 0 self.wait_until_next_sync = False diff --git a/tests/api_jobs/test_updatestar.py b/tests/api_jobs/test_updatestar.py index 6f95efb347..17c8a798f7 100644 --- a/tests/api_jobs/test_updatestar.py +++ b/tests/api_jobs/test_updatestar.py @@ -1,6 +1,9 @@ import pytest -from securedrop_client.api_jobs.updatestar import UpdateStarJob, UpdateStarJobException +from securedrop_client.api_jobs.updatestar import UpdateStarJob, UpdateStarJobError, \ + UpdateStarJobTimeoutError +from sdclientapi import RequestTimeoutError, ServerConnectionError + from tests import factory @@ -61,9 +64,9 @@ def test_unstar_if_star(homedir, mocker, session, session_maker): api_client.remove_star.assert_called_once_with(mock_sdk_source) -def test_failure_to_star(homedir, mocker, session, session_maker): +def test_call_api_raises_UpdateStarJobError(homedir, mocker, session, session_maker): ''' - Check if we call remove_star method if a source is stared. + Check that UpdateStarJobError is raised if remove_star fails due to an exception. ''' source = factory.Source() source.is_starred = True @@ -80,5 +83,26 @@ def test_failure_to_star(homedir, mocker, session, session_maker): source.is_starred ) - with pytest.raises(UpdateStarJobException): + with pytest.raises(UpdateStarJobError): + job.call_api(api_client, session) + + +@pytest.mark.parametrize("exception", [RequestTimeoutError, ServerConnectionError]) +def test_call_api_raises_UpdateStarJobTimeoutError(mocker, session, exception): + ''' + Check that UpdateStarJobTimeoutError is raised if remove_star fails due to a timeout. + ''' + source = factory.Source() + source.is_starred = True + session.add(source) + session.commit() + + api_client = mocker.MagicMock() + api_client.remove_star = mocker.MagicMock() + api_client.remove_star.side_effect = exception() + + job = UpdateStarJob(source.uuid, source.is_starred) + + error = f'Failed to update star on source {source.uuid} due to error' + with pytest.raises(UpdateStarJobTimeoutError, match=error): job.call_api(api_client, session) diff --git a/tests/gui/test_init.py b/tests/gui/test_init.py index 8858285fe3..a16096379d 100644 --- a/tests/gui/test_init.py +++ b/tests/gui/test_init.py @@ -28,24 +28,6 @@ def test_SvgToggleButton_init(mocker): setIconSize_fn.assert_called_once_with(svg_size) -def test_SvgToggleButton_enable(mocker): - """ - Ensure enable. - """ - stb = SvgToggleButton(on='mock_on', off='mock_off') - stb.enable() - assert stb.isEnabled() is True - - -def test_SvgToggleButton_disable(mocker): - """ - Ensure disable. - """ - stb = SvgToggleButton(on='mock_on', off='mock_off') - stb.disable() - assert stb.isEnabled() is False - - def test_SvgToggleButton_toggle(mocker): """ Make sure we're not calling this a toggle button for no reason. @@ -95,24 +77,6 @@ def test_SvgPushButton_init(mocker): setIconSize_fn.assert_called_once_with(svg_size) -def test_SvgPushButton_enable(mocker): - """ - Ensure enable. - """ - spb = SvgPushButton(normal='mock1', disabled='mock2', active='mock3', selected='mock4') - spb.enable() - assert spb.isEnabled() is True - - -def test_SvgPushButton_disable(mocker): - """ - Ensure disable. - """ - spb = SvgPushButton(normal='mock1', disabled='mock2', active='mock3', selected='mock4') - spb.disable() - assert spb.isEnabled() is False - - def test_SvgLabel_init(mocker): """ Ensure SvgLabel calls the expected methods correctly to set the icon and size. diff --git a/tests/gui/test_widgets.py b/tests/gui/test_widgets.py index adb7e3a5c4..528bc2f3ed 100644 --- a/tests/gui/test_widgets.py +++ b/tests/gui/test_widgets.py @@ -1133,65 +1133,158 @@ def test_SourceWidget_uses_SecureQLabel(mocker): def test_StarToggleButton_init_source_starred(mocker): controller = mocker.MagicMock() - source = factory.Source() - source.is_starred = True + source = factory.Source(is_starred=True) - stb = StarToggleButton(controller, source) + stb = StarToggleButton(controller, source.uuid, source.is_starred) - assert stb.source == source + assert stb.source_uuid == source.uuid assert stb.isChecked() is True def test_StarToggleButton_init_source_unstarred(mocker): controller = mocker.MagicMock() - source = factory.Source() - source.is_starred = False + source = factory.Source(is_starred=False) - stb = StarToggleButton(controller, source) + stb = StarToggleButton(controller, source.uuid, source.is_starred) - assert stb.source == source + assert stb.source_uuid == source.uuid assert stb.isChecked() is False -def test_StarToggleButton_eventFilter(mocker): +def test_StarToggleButton_eventFilter_when_checked(mocker): """ - Ensure the hover events are handled properly. + Ensure the hover events are handled properly when star is checked and online. """ controller = mocker.MagicMock() - stb = StarToggleButton(controller=controller, source=mocker.MagicMock()) + controller.is_authenticated = True + stb = StarToggleButton(controller, 'mock_uuid', True) + stb.pressed = mocker.MagicMock() stb.setIcon = mocker.MagicMock() stb.set_icon = mocker.MagicMock() + load_icon_fn = mocker.patch("securedrop_client.gui.widgets.load_icon") + # Hover enter test_event = QEvent(QEvent.HoverEnter) stb.eventFilter(stb, test_event) assert stb.setIcon.call_count == 1 + load_icon_fn.assert_called_once_with('star_hover.svg') + # Hover leave test_event = QEvent(QEvent.HoverLeave) stb.eventFilter(stb, test_event) stb.set_icon.assert_called_once_with(on='star_on.svg', off='star_off.svg') - # Hover leave when disabled - stb.disable() + # Authentication change + stb.on_authentication_changed(authenticated=True) + assert stb.isCheckable() is True + stb.pressed.disconnect.assert_called_once_with() + stb.pressed.connect.assert_called_once_with(stb.on_pressed) + + +def test_StarToggleButton_eventFilter_when_not_checked(mocker): + """ + Ensure the hover events are handled properly when star is checked and online. + """ + controller = mocker.MagicMock() + controller.is_authenticated = True + stb = StarToggleButton(controller, 'mock_uuid', False) + stb.pressed = mocker.MagicMock() + stb.setIcon = mocker.MagicMock() + stb.set_icon = mocker.MagicMock() + load_icon_fn = mocker.patch("securedrop_client.gui.widgets.load_icon") + + # Hover enter + test_event = QEvent(QEvent.HoverEnter) + stb.eventFilter(stb, test_event) + assert stb.setIcon.call_count == 1 + load_icon_fn.assert_called_once_with('star_hover.svg') + + # Hover leave test_event = QEvent(QEvent.HoverLeave) stb.eventFilter(stb, test_event) + stb.set_icon.assert_called_once_with(on='star_on.svg', off='star_off.svg') + + # Authentication change + stb.on_authentication_changed(authenticated=True) + assert stb.isCheckable() is True + stb.pressed.disconnect.assert_called_once_with() + stb.pressed.connect.assert_called_once_with(stb.on_pressed) + + +def test_StarToggleButton_eventFilter_when_checked_and_offline(mocker): + """ + Ensure the hover events do not change the icon when offline and that the star icon is set to + off='star_on.svg' when checked and offline. + """ + controller = mocker.MagicMock() + stb = StarToggleButton(controller, 'mock_uuid', True) + stb.pressed = mocker.MagicMock() + stb.setIcon = mocker.MagicMock() + stb.set_icon = mocker.MagicMock() + load_icon_fn = mocker.patch("securedrop_client.gui.widgets.load_icon") + + # Authentication change + stb.on_authentication_changed(authenticated=False) + assert stb.isCheckable() is False stb.set_icon.assert_called_with(on='star_on.svg', off='star_on.svg') + stb.pressed.disconnect.assert_called_once_with() + stb.pressed.connect.assert_called_once_with(stb.on_pressed_offline) + + # Hover enter + test_event = QEvent(QEvent.HoverEnter) + stb.eventFilter(stb, test_event) + stb.setIcon.assert_not_called() + load_icon_fn.assert_not_called() + + # Hover leave + test_event = QEvent(QEvent.HoverLeave) + stb.eventFilter(stb, test_event) + stb.setIcon.assert_not_called() + + +def test_StarToggleButton_eventFilter_when_not_checked_and_offline(mocker): + """ + Ensure the hover events do not change the icon when offline and that the star icon is set to + off='star_on.svg' when unchecked and offline. + """ + controller = mocker.MagicMock() + stb = StarToggleButton(controller, 'mock_uuid', False) + stb.pressed = mocker.MagicMock() + stb.setIcon = mocker.MagicMock() + stb.set_icon = mocker.MagicMock() + load_icon_fn = mocker.patch("securedrop_client.gui.widgets.load_icon") + + # Authentication change + stb.on_authentication_changed(authenticated=False) + assert stb.isCheckable() is False + stb.pressed.disconnect.assert_called_once_with() + stb.pressed.connect.assert_called_once_with(stb.on_pressed_offline) + + # Hover enter + test_event = QEvent(QEvent.HoverEnter) + stb.eventFilter(stb, test_event) + stb.setIcon.assert_not_called() + load_icon_fn.assert_not_called() + + # Hover leave + test_event = QEvent(QEvent.HoverLeave) + stb.eventFilter(stb, test_event) + stb.setIcon.assert_not_called() def test_StarToggleButton_on_authentication_changed_while_authenticated_and_checked(mocker): """ - If on_authentication_changed is set up correctly, then calling toggle on a checked button should - result in the button being unchecked. + If on_authentication_changed is set up correctly, then toggling a checked button should result + in the button being unchecked. """ controller = mocker.MagicMock() - source = mocker.MagicMock() - stb = StarToggleButton(controller, source=source) - stb.setChecked(True) - stb.on_toggle = mocker.MagicMock() + stb = StarToggleButton(controller, 'mock_uuid', True) + stb.on_pressed = mocker.MagicMock() stb.on_authentication_changed(authenticated=True) - stb.toggle() + stb.click() - assert stb.on_toggle.called is True + stb.on_pressed.assert_called_once_with() assert stb.isChecked() is False @@ -1201,97 +1294,231 @@ def test_StarToggleButton_on_authentication_changed_while_authenticated_and_not_ should result in the button being unchecked. """ controller = mocker.MagicMock() - source = mocker.MagicMock() - source.is_starred = False - stb = StarToggleButton(controller, source=source) - stb.setChecked(False) - stb.on_toggle = mocker.MagicMock() - assert stb.isChecked() is False - + stb = StarToggleButton(controller, 'mock_uuid', False) + stb.on_pressed = mocker.MagicMock() stb.on_authentication_changed(authenticated=True) - assert stb.isChecked() is False - stb.toggle() + stb.click() - assert stb.on_toggle.called is True + stb.on_pressed.assert_called_once_with() assert stb.isChecked() is True -def test_StarToggleButton_on_authentication_changed_while_offline_mode(mocker): +def test_StarToggleButton_on_authentication_changed_while_offline_mode_and_not_checked(mocker): """ Ensure on_authentication_changed is set up correctly for offline mode. """ controller = mocker.MagicMock() - source = mocker.MagicMock() - stb = StarToggleButton(controller, source=source) - stb.on_toggle_offline = mocker.MagicMock() - stb.on_toggle = mocker.MagicMock() + stb = StarToggleButton(controller, 'mock_uuid', False) + stb.on_pressed_offline = mocker.MagicMock() + stb.on_pressed = mocker.MagicMock() + stb.on_authentication_changed(authenticated=False) + + stb.click() + + stb.on_pressed_offline.assert_called_once_with() + stb.on_pressed.assert_not_called() + assert stb.isChecked() is False + +def test_StarToggleButton_on_authentication_changed_while_offline_mode_and_checked(mocker): + """ + Ensure on_authentication_changed is set up correctly for offline mode. + """ + controller = mocker.MagicMock() + stb = StarToggleButton(controller, 'mock_uuid', True) + stb.on_pressed_offline = mocker.MagicMock() + stb.on_pressed = mocker.MagicMock() stb.on_authentication_changed(authenticated=False) + stb.click() - assert stb.on_toggle_offline.called is True - assert stb.on_toggle.called is False + stb.on_pressed_offline.assert_called_once_with() + stb.on_pressed.assert_not_called() + assert stb.isCheckable() is False + assert stb.is_starred is True -def test_StarToggleButton_on_toggle(mocker): +def test_StarToggleButton_on_pressed_toggles_to_starred(mocker): """ - Ensure correct star icon images are loaded for the enabled button. + Ensure pressing the star button toggles from unstarred to starred. """ controller = mocker.MagicMock() - source = mocker.MagicMock() - stb = StarToggleButton(controller, source) + stb = StarToggleButton(controller, 'mock_uuid', False) - stb.on_toggle() + stb.click() - stb.controller.update_star.assert_called_once_with(source, stb.on_update) + stb.controller.update_star.assert_called_once_with('mock_uuid', False) + assert stb.isChecked() -def test_StarToggleButton_on_toggle_offline(mocker): +def test_StarToggleButton_on_pressed_toggles_to_unstarred(mocker): + """ + Ensure pressing the star button toggles from starred to unstarred. + """ + controller = mocker.MagicMock() + stb = StarToggleButton(controller, 'mock_uuid', True) + + stb.click() + + stb.controller.update_star.assert_called_once_with('mock_uuid', True) + assert not stb.isChecked() + + +def test_StarToggleButton_on_pressed_offline(mocker): """ Ensure toggle is disabled when offline. """ controller = mocker.MagicMock() - source = mocker.MagicMock() - stb = StarToggleButton(controller, source) + controller.is_authenticated = False + stb = StarToggleButton(controller, 'mock_uuid', True) + + stb.click() - stb.on_toggle_offline() stb.controller.on_action_requiring_login.assert_called_once_with() -def test_StarToggleButton_on_toggle_offline_when_checked(mocker): +def test_StarToggleButton_on_pressed_offline_when_checked(mocker): """ Ensure correct star icon images are loaded for the disabled button. """ controller = mocker.MagicMock() - source = mocker.MagicMock() - source.is_starred = True - stb = StarToggleButton(controller, source) + controller.is_authenticated = False + source = factory.Source(is_starred=True) + stb = StarToggleButton(controller, source.uuid, source.is_starred) set_icon_fn = mocker.patch('securedrop_client.gui.SvgToggleButton.set_icon') - # go offline stb.on_authentication_changed(False) assert stb.isCheckable() is False set_icon_fn.assert_called_with(on='star_on.svg', off='star_on.svg') - stb.on_toggle_offline() + stb.click() stb.controller.on_action_requiring_login.assert_called_once_with() -def test_StarToggleButton_on_update(mocker): +def test_StarToggleButton_update(mocker): """ - Ensure the on_update callback updates the state of the source and UI - element to the current "enabled" state. + Ensure update syncs the star state with the server if there are no pending jobs and we're not + waiting until the next sync (in order to avoid the "ghost" issue where update is called with an + outdated state between a star job finishing and a sync). """ controller = mocker.MagicMock() - source = mocker.MagicMock() - source.is_starred = True - stb = StarToggleButton(controller, source) - stb.setChecked = mocker.MagicMock() - stb.on_update(("uuid", False)) - assert source.is_starred is False - stb.controller.update_sources.assert_called_once_with() - stb.setChecked.assert_called_once_with(False) + controller.is_authenticated = True + stb = StarToggleButton(controller, 'mock_uuid', True) + + # Should not change because we wait until next sync + stb.pending_count = 0 + stb.wait_until_next_sync = True + stb.update(False) + assert stb.isChecked() is True + stb.update(True) + assert stb.isChecked() is True + + # Should update to match value provided by update because there are no pending star jobs and + # wait_until_next_sync is False, meaning a sync already occurred after the star job finished + stb.setChecked(True) + stb.pending_count = 0 + stb.wait_until_next_sync = False + stb.update(False) + assert stb.isChecked() is False + stb.update(True) + assert stb.isChecked() is True + + # Should not change because there are pending star jobs + stb.setChecked(True) + stb.pending_count = 1 + stb.wait_until_next_sync = True + stb.update(False) + assert stb.isChecked() is True + stb.update(True) + assert stb.isChecked() is True + # Still should not change because there are pending star jobs + stb.wait_until_next_sync = False + stb.update(False) + assert stb.isChecked() is True + stb.update(True) + assert stb.isChecked() is True + + +def test_StarToggleButton_update_when_not_authenticated(mocker): + """ + Ensure the button state does not change if the user is not authenticated. + """ + controller = mocker.MagicMock() + controller.is_authenticated = False + source = factory.Source(is_starred=True) + stb = StarToggleButton(controller, source.uuid, source.is_starred) + + # Button stays checked + stb.update(False) + assert stb.is_starred is True + + # Button stays unchecked + stb.is_starred = False + stb.update(True) + assert stb.is_starred is False + + +def test_StarToggleButton_on_star_update_failed(mocker): + ''' + Ensure the button is toggled to the state provided in the failure handler and that the pending + count is decremented if the source uuid matches. + ''' + controller = mocker.MagicMock() + controller.is_authenticated = True + stb = StarToggleButton(controller, 'mock_uuid', False) + + stb.click() + assert stb.is_starred is True + assert stb.pending_count == 1 + stb.on_star_update_failed('mock_uuid', is_starred=False) + assert stb.is_starred is False + assert stb.pending_count == 0 + + +def test_StarToggleButton_on_star_update_failed_for_non_matching_source_uuid(mocker): + ''' + Ensure the button is not toggled and that the pending count stays the same if the source uuid + does not match. + ''' + controller = mocker.MagicMock() + controller.is_authenticated = True + stb = StarToggleButton(controller, 'mock_uuid', False) + + stb.click() + assert stb.is_starred is True + assert stb.pending_count == 1 + stb.on_star_update_failed('some_other_uuid', is_starred=False) + assert stb.is_starred is True + assert stb.pending_count == 1 + + +def test_StarToggleButton_on_star_update_successful(mocker): + ''' + Ensure that the pending count is decremented if the source uuid matches. + ''' + controller = mocker.MagicMock() + controller.is_authenticated = True + stb = StarToggleButton(controller, 'mock_uuid', True) + + stb.click() + assert stb.pending_count == 1 + stb.on_star_update_successful('mock_uuid') + assert stb.pending_count == 0 + + +def test_StarToggleButton_on_star_update_successful_for_non_matching_source_uuid(mocker): + ''' + Ensure that the pending count is not decremented if the source uuid does not match. + ''' + controller = mocker.MagicMock() + controller.is_authenticated = True + stb = StarToggleButton(controller, 'mock_uuid', True) + + stb.click() + assert stb.pending_count == 1 + stb.on_star_update_successful('some_other_uuid') + assert stb.pending_count == 1 def test_LoginDialog_setup(mocker, i18n): diff --git a/tests/test_logic.py b/tests/test_logic.py index c65ca8658d..42d217dda5 100644 --- a/tests/test_logic.py +++ b/tests/test_logic.py @@ -13,9 +13,9 @@ from securedrop_client import db from securedrop_client.logic import APICallRunner, Controller, TIME_BETWEEN_SHOWING_LAST_SYNC_MS from securedrop_client.api_jobs.base import ApiInaccessibleError -from securedrop_client.api_jobs.downloads import ( - DownloadChecksumMismatchException, DownloadDecryptionException, DownloadException -) +from securedrop_client.api_jobs.downloads import DownloadChecksumMismatchException, \ + DownloadDecryptionException, DownloadException +from securedrop_client.api_jobs.updatestar import UpdateStarJobError, UpdateStarJobTimeoutError from securedrop_client.api_jobs.uploads import SendReplyJobError, SendReplyJobTimeoutError with open(os.path.join(os.path.dirname(__file__), 'files', 'test-key.gpg.pub.asc')) as f: @@ -571,23 +571,41 @@ def test_Controller_on_update_star_success(homedir, config, mocker, session_make """ mock_gui = mocker.MagicMock() co = Controller('http://localhost', mock_gui, session_maker, homedir) - result = True - co.call_reset = mocker.MagicMock() - co.on_update_star_success(result) + co.star_update_failed = mocker.MagicMock() + co.on_update_star_success('mock_uuid') -def test_Controller_on_update_star_failed(homedir, config, mocker, session_maker): + +def test_Controller_on_update_star_failed(homedir, config, mocker): """ - If the starring does not occur properly, then an error should appear - on the error status sidebar, and a sync will not occur. - Using the `config` fixture to ensure the config is written to disk. + Check that if starring fails then the failure signal is emitted and the error bar is updated + with a failure message. """ - mock_gui = mocker.MagicMock() - co = Controller('http://localhost', mock_gui, session_maker, homedir) - result = Exception('boom') - co.call_reset = mocker.MagicMock() - co.on_update_star_failure(result) - mock_gui.update_error_status.assert_called_once_with('Failed to update star.') + gui = mocker.MagicMock() + co = Controller('http://localhost', gui, mocker.MagicMock(), homedir) + co.star_update_failed = mocker.MagicMock() + + error = UpdateStarJobError('mock_message', 'mock_uuid', True) + co.on_update_star_failure(error) + + co.star_update_failed.emit.assert_called_once_with(error.source_uuid, error.is_starred) + gui.update_error_status.assert_called_once_with('Failed to update star.') + + +def test_Controller_on_update_star_failed_due_to_timeout(homedir, config, mocker): + """ + Ensure the failure signal is not emitted and the error bar is not updated if the star fails due + to a timeout (regression test). + """ + gui = mocker.MagicMock() + co = Controller('http://localhost', gui, mocker.MagicMock(), homedir) + co.star_update_failed = mocker.MagicMock() + + error = UpdateStarJobTimeoutError('mock_message', 'mock_uuid', True) + co.on_update_star_failure(error) + + co.star_update_failed.emit.assert_not_called() + gui.update_error_status.assert_not_called() def test_Controller_invalidate_token(mocker, homedir, session_maker): @@ -1560,36 +1578,26 @@ def test_Controller_call_update_star_success(homedir, config, mocker, session_ma co.call_api = mocker.MagicMock() co.api = mocker.MagicMock() - mock_success_signal = mocker.MagicMock() - mock_failure_signal = mocker.MagicMock() - mock_job = mocker.MagicMock(success_signal=mock_success_signal, - failure_signal=mock_failure_signal) - mock_job_cls = mocker.patch( - "securedrop_client.logic.UpdateStarJob", return_value=mock_job) + star_update_successful = mocker.MagicMock() + star_update_failed = mocker.MagicMock() + mock_job = mocker.MagicMock(success_signal=star_update_successful, + failure_signal=star_update_failed) + mock_job_cls = mocker.patch("securedrop_client.logic.UpdateStarJob", return_value=mock_job) mock_queue = mocker.patch.object(co, 'api_job_queue') source = factory.Source() session.add(source) session.commit() - mock_callback = mocker.MagicMock() - - co.update_star(source, mock_callback) - - mock_job_cls.assert_called_once_with( - source.uuid, - source.is_starred - ) + co.update_star(source.uuid, source.is_starred) + mock_job_cls.assert_called_once_with(source.uuid, source.is_starred) mock_queue.enqueue.assert_called_once_with(mock_job) - assert mock_success_signal.connect.call_count == 2 - cal = mock_success_signal.connect.call_args_list - assert cal[0][0][0] == co.on_update_star_success - assert cal[0][1]['type'] == Qt.QueuedConnection - assert cal[1][0][0] == mock_callback - assert cal[1][1]['type'] == Qt.QueuedConnection - mock_failure_signal.connect.assert_called_once_with( + assert star_update_successful.connect.call_count == 1 + star_update_failed.connect.assert_called_once_with( co.on_update_star_failure, type=Qt.QueuedConnection) + star_update_successful.connect.assert_called_once_with( + co.on_update_star_success, type=Qt.QueuedConnection) def test_Controller_run_printer_preflight_checks(homedir, mocker, session, source):