Skip to content

Commit 8f93793

Browse files
Suppress expected-skip notifications for scheduled backups
Use structured level field in create.py for WiFi and metered connection skips instead of fragile string comparison. Add unit tests covering both skip cases and notification suppression. Fixes #2231
1 parent 07ce18c commit 8f93793

5 files changed

Lines changed: 64 additions & 7 deletions

File tree

src/vorta/borg/create.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ def prepare(cls, profile):
122122
)
123123
if wifi_is_disallowed.count() > 0 and profile.repo.is_remote_repo():
124124
ret['message'] = trans_late('messages', 'Current Wifi is not allowed.')
125+
ret['level'] = 'info'
125126
return ret
126127

127128
if (
@@ -130,6 +131,7 @@ def prepare(cls, profile):
130131
and network_status_monitor.is_network_metered()
131132
):
132133
ret['message'] = trans_late('messages', 'Not running backup over metered connection.')
134+
ret['level'] = 'info'
133135
return ret
134136

135137
ret['profile'] = profile

src/vorta/scheduler.py

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -448,13 +448,19 @@ def create_backup(self, profile_id):
448448
job.result.connect(self.notify)
449449
self.app.jobs_manager.add_job(job)
450450
else:
451-
logger.error('Conditions for backup not met. Aborting.')
452-
logger.error(msg['message'])
453-
notifier.deliver(
454-
self.tr('Vorta Backup'),
455-
translate('messages', msg['message']),
456-
level='error',
457-
)
451+
# Default to 'error': unexpected failures notify.
452+
# Expected skips (WiFi/metered) use 'info' to suppress.
453+
level = msg.get('level', 'error')
454+
if level == 'error':
455+
logger.error('Conditions for backup not met. Aborting.')
456+
logger.error(msg['message'])
457+
notifier.deliver(
458+
self.tr('Vorta Backup'),
459+
translate('messages', msg['message']),
460+
level='error',
461+
)
462+
else:
463+
logger.info('Backup skipped: %s', msg['message'])
458464
self.pause(profile_id)
459465

460466
def notify(self, result):

src/vorta/views/archive_tab.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ def __init__(self, parent=None, app=None, profile_provider=None):
155155
self.prune_keep_within.editingFinished.connect(self.save_prune_setting)
156156

157157
self.populate_from_profile()
158+
self.selected_archives = None # TODO: remove unused variable
158159
self.set_icons()
159160

160161
# Connect to events

tests/unit/test_create.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,29 @@ def test_create_paths_from_command():
5151
'echo',
5252
TEST_SOURCE_DIR,
5353
]
54+
55+
56+
def test_prepare_returns_info_level_when_wifi_disallowed(mocker):
57+
"""Test that prepare() returns level='info' when WiFi is not allowed."""
58+
default_profile = BackupProfileModel.get()
59+
mock_monitor = mocker.MagicMock()
60+
mock_monitor.get_current_wifi.return_value = 'blocked_wifi'
61+
mocker.patch('vorta.borg.create.get_network_status_monitor', return_value=mock_monitor)
62+
mocker.patch(
63+
'vorta.borg.create.WifiSettingModel.select',
64+
return_value=mocker.MagicMock(where=lambda *a, **kw: mocker.MagicMock(count=lambda: 1)),
65+
)
66+
result = BorgCreateJob.prepare(default_profile)
67+
assert result.get('level') == 'info'
68+
69+
70+
def test_prepare_returns_info_level_when_metered_connection(mocker):
71+
"""Test that prepare() returns level='info' for metered connections."""
72+
default_profile = BackupProfileModel.get()
73+
default_profile.dont_run_on_metered_networks = True
74+
mock_monitor = mocker.MagicMock()
75+
mock_monitor.get_current_wifi.return_value = None
76+
mock_monitor.is_network_metered.return_value = True
77+
mocker.patch('vorta.borg.create.get_network_status_monitor', return_value=mock_monitor)
78+
result = BorgCreateJob.prepare(default_profile)
79+
assert result.get('level') == 'info'

tests/unit/test_scheduler.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,3 +239,25 @@ def test_missed_startup(qapp, qtbot, window_load, clockmock, now, hour, minute,
239239
assert len(event_times) == 2
240240
else:
241241
assert len(event_times) == 1
242+
243+
244+
@prepare
245+
def test_create_backup_no_error_notification_on_info_level(qapp, qtbot, mocker, borg_json_output):
246+
"""Test that notifier.deliver() is not called with level='error' when
247+
prepare() returns level='info' (e.g. WiFi disallowed or metered connection)."""
248+
mocker.patch(
249+
'vorta.scheduler.BorgCreateJob.prepare',
250+
return_value={
251+
'ok': False,
252+
'message': 'Current Wifi is not allowed.',
253+
'level': 'info',
254+
},
255+
)
256+
notifier_mock = mocker.patch('vorta.notifications.VortaNotifications.pick')
257+
mock_notifier = mocker.MagicMock()
258+
notifier_mock.return_value = mock_notifier
259+
260+
qapp.scheduler.create_backup(1)
261+
262+
for call in mock_notifier.deliver.call_args_list:
263+
assert call.kwargs.get('level') != 'error'

0 commit comments

Comments
 (0)