Skip to content

Commit e3a4009

Browse files
Merge branch 'master' into feature/consolidation-issue-2474
2 parents 989ac63 + f0ec8b8 commit e3a4009

5 files changed

Lines changed: 78 additions & 9 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
@@ -450,13 +450,19 @@ def create_backup(self, profile_id: int) -> None:
450450
job.result.connect(self.notify)
451451
self.app.jobs_manager.add_job(job)
452452
else:
453-
logger.error('Conditions for backup not met. Aborting.')
454-
logger.error(msg['message'])
455-
notifier.deliver(
456-
self.tr('Vorta Backup'),
457-
translate('messages', msg['message']),
458-
level='error',
459-
)
453+
# Default to 'error': unexpected failures notify.
454+
# Expected skips (WiFi/metered) use 'info' to suppress.
455+
level = msg.get('level', 'error')
456+
if level == 'error':
457+
logger.error('Conditions for backup not met. Aborting.')
458+
logger.error(msg['message'])
459+
notifier.deliver(
460+
self.tr('Vorta Backup'),
461+
translate('messages', msg['message']),
462+
level='error',
463+
)
464+
else:
465+
logger.info('Backup skipped: %s', msg['message'])
460466
self.pause(profile_id)
461467

462468
def notify(self, result: dict[str, Any]) -> None:

src/vorta/views/source_tab.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,11 @@ def set_path_info(self, path, data_size, files_count):
151151
files_count = int(files_count)
152152

153153
for item in items:
154-
db_item = SourceFileModel.get(dir=path, profile=self.profile())
154+
try:
155+
db_item = SourceFileModel.get(dir=path, profile=self.profile())
156+
except SourceFileModel.DoesNotExist:
157+
continue
158+
155159
if QFileInfo(path).isDir():
156160
self.sourceFilesWidget.item(item.row(), SourceColumn.FilesCount).setText(format(files_count))
157161
db_item.path_isdir = True
@@ -321,13 +325,22 @@ def source_remove(self):
321325
indexes.sort()
322326
# remove each selected row, starting with the highest index (otherwise, higher indexes become invalid)
323327
for index in reversed(indexes):
328+
path = self.sourceFilesWidget.item(index.row(), SourceColumn.Path).text()
324329
db_item = SourceFileModel.get(
325-
dir=self.sourceFilesWidget.item(index.row(), SourceColumn.Path).text(),
330+
dir=path,
326331
profile=profile,
327332
)
328333
db_item.delete_instance()
329334
self.sourceFilesWidget.removeRow(index.row())
330335

336+
for thrd in self.updateThreads[:]:
337+
if thrd.objectName() == path:
338+
try:
339+
thrd.signal.disconnect(self.set_path_info)
340+
except (RuntimeError, TypeError):
341+
pass
342+
self.updateThreads.remove(thrd)
343+
331344
logger.debug(f"Removed source in row {index.row()}")
332345
self.update_total_size()
333346

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+
# The error notification should be suppressed for an expected skip.
262+
assert mock_notifier.deliver.call_count == 1
263+
assert mock_notifier.deliver.call_args.kwargs.get('level') != 'error'

0 commit comments

Comments
 (0)