synodic-client 0.0.1.dev61__tar.gz → 0.0.1.dev62__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/PKG-INFO +1 -1
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/pyproject.toml +1 -1
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/synodic_client/application/screen/settings.py +9 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/synodic_client/application/screen/tool_update_controller.py +6 -1
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/synodic_client/application/update_controller.py +5 -2
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/tests/unit/qt/test_settings.py +43 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/tests/unit/qt/test_update_controller.py +54 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/LICENSE.md +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/README.md +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/synodic_client/__init__.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/synodic_client/__main__.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/synodic_client/application/__init__.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/synodic_client/application/bootstrap.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/synodic_client/application/data.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/synodic_client/application/icon.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/synodic_client/application/init.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/synodic_client/application/instance.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/synodic_client/application/qt.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/synodic_client/application/schema.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/synodic_client/application/screen/__init__.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/synodic_client/application/screen/action_card.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/synodic_client/application/screen/card.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/synodic_client/application/screen/install.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/synodic_client/application/screen/install_workers.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/synodic_client/application/screen/log_panel.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/synodic_client/application/screen/plugin_row.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/synodic_client/application/screen/projects.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/synodic_client/application/screen/schema.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/synodic_client/application/screen/screen.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/synodic_client/application/screen/sidebar.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/synodic_client/application/screen/spinner.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/synodic_client/application/screen/tray.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/synodic_client/application/screen/update_banner.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/synodic_client/application/theme.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/synodic_client/application/uri.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/synodic_client/application/workers.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/synodic_client/cli.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/synodic_client/client.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/synodic_client/config.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/synodic_client/logging.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/synodic_client/protocol.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/synodic_client/py.typed +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/synodic_client/resolution.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/synodic_client/schema.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/synodic_client/startup.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/synodic_client/updater.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/tests/__init__.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/tests/conftest.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/tests/unit/__init__.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/tests/unit/qt/__init__.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/tests/unit/qt/conftest.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/tests/unit/qt/test_action_card.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/tests/unit/qt/test_gather_packages.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/tests/unit/qt/test_install_preview.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/tests/unit/qt/test_log_panel.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/tests/unit/qt/test_logging.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/tests/unit/qt/test_preview_model.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/tests/unit/qt/test_sidebar.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/tests/unit/qt/test_tray_window_show.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/tests/unit/qt/test_update_banner.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/tests/unit/qt/test_update_feedback.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/tests/unit/test_cli.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/tests/unit/test_client_updater.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/tests/unit/test_client_version.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/tests/unit/test_config.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/tests/unit/test_examples.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/tests/unit/test_init.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/tests/unit/test_install.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/tests/unit/test_resolution.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/tests/unit/test_updater.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/tests/unit/test_uri.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/tests/unit/test_workers.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/tests/unit/windows/__init__.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/tests/unit/windows/conftest.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/tests/unit/windows/test_protocol.py +0 -0
- {synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/tests/unit/windows/test_startup.py +0 -0
|
@@ -305,6 +305,15 @@ class SettingsWindow(QMainWindow):
|
|
|
305
305
|
"""Re-enable the *Check for Updates* button after a check completes."""
|
|
306
306
|
self._check_updates_btn.setEnabled(True)
|
|
307
307
|
|
|
308
|
+
def update_config(self, config: ResolvedConfig) -> None:
|
|
309
|
+
"""Replace the internal config snapshot without emitting signals.
|
|
310
|
+
|
|
311
|
+
Called by controllers that persist timestamps so that the next
|
|
312
|
+
:meth:`sync_from_config` sees fresh data instead of the stale
|
|
313
|
+
snapshot captured at construction time.
|
|
314
|
+
"""
|
|
315
|
+
self._config = config
|
|
316
|
+
|
|
308
317
|
def set_last_checked(self, timestamp: str) -> None:
|
|
309
318
|
"""Update the *last updated* label from an ISO 8601 timestamp."""
|
|
310
319
|
relative = _format_relative_time(timestamp)
|
|
@@ -382,7 +382,12 @@ class ToolUpdateOrchestrator:
|
|
|
382
382
|
for pkg_name in result.updated_packages:
|
|
383
383
|
key = f'{plugin_name}/{pkg_name}' if plugin_name else pkg_name
|
|
384
384
|
existing[key] = now
|
|
385
|
-
update_user_config(last_tool_updates=existing)
|
|
385
|
+
resolved = update_user_config(last_tool_updates=existing)
|
|
386
|
+
# Refresh the config on the tools view so the next rebuild
|
|
387
|
+
# picks up the updated timestamps instead of stale data.
|
|
388
|
+
tools_view_ref = self._window.tools_view
|
|
389
|
+
if tools_view_ref is not None:
|
|
390
|
+
tools_view_ref._config = resolved
|
|
386
391
|
|
|
387
392
|
# Clear updating state on widgets
|
|
388
393
|
tools_view = self._window.tools_view
|
|
@@ -132,7 +132,8 @@ class UpdateController:
|
|
|
132
132
|
def _persist_check_timestamp(self) -> None:
|
|
133
133
|
"""Persist the current time as *last_client_update* and refresh the label."""
|
|
134
134
|
ts = datetime.now(UTC).isoformat()
|
|
135
|
-
update_user_config(last_client_update=ts)
|
|
135
|
+
resolved = update_user_config(last_client_update=ts)
|
|
136
|
+
self._settings_window.update_config(resolved)
|
|
136
137
|
self._settings_window.set_last_checked(ts)
|
|
137
138
|
|
|
138
139
|
def _report_error(self, message: str, *, silent: bool) -> None:
|
|
@@ -329,7 +330,9 @@ class UpdateController:
|
|
|
329
330
|
|
|
330
331
|
# Persist the client-update timestamp (actual update downloaded)
|
|
331
332
|
ts = datetime.now(UTC).isoformat()
|
|
332
|
-
update_user_config(last_client_update=ts)
|
|
333
|
+
resolved = update_user_config(last_client_update=ts)
|
|
334
|
+
self._settings_window.update_config(resolved)
|
|
335
|
+
self._settings_window.set_last_checked(ts)
|
|
333
336
|
|
|
334
337
|
self._pending_version = version
|
|
335
338
|
|
|
@@ -404,3 +404,46 @@ class TestCheckForUpdatesButton:
|
|
|
404
404
|
window.set_checking()
|
|
405
405
|
assert window._check_updates_btn.isEnabled() is False
|
|
406
406
|
assert window._update_status_label.text() == 'Checking\u2026'
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
# ---------------------------------------------------------------------------
|
|
410
|
+
# update_config — silent config refresh
|
|
411
|
+
# ---------------------------------------------------------------------------
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
class TestUpdateConfig:
|
|
415
|
+
"""Verify that update_config refreshes _config without emitting signals."""
|
|
416
|
+
|
|
417
|
+
@staticmethod
|
|
418
|
+
def test_update_config_replaces_internal_config() -> None:
|
|
419
|
+
"""update_config should replace _config with the new snapshot."""
|
|
420
|
+
window = _make_window(_make_config(last_client_update=None))
|
|
421
|
+
new_config = _make_config(last_client_update='2026-03-09T12:00:00+00:00')
|
|
422
|
+
|
|
423
|
+
window.update_config(new_config)
|
|
424
|
+
|
|
425
|
+
assert window._config is new_config
|
|
426
|
+
assert window._config.last_client_update == '2026-03-09T12:00:00+00:00'
|
|
427
|
+
|
|
428
|
+
@staticmethod
|
|
429
|
+
def test_update_config_does_not_emit_settings_changed() -> None:
|
|
430
|
+
"""update_config must NOT emit settings_changed to avoid circular reinit."""
|
|
431
|
+
window = _make_window()
|
|
432
|
+
signal_spy = MagicMock()
|
|
433
|
+
window.settings_changed.connect(signal_spy)
|
|
434
|
+
|
|
435
|
+
window.update_config(_make_config(update_channel='dev'))
|
|
436
|
+
|
|
437
|
+
signal_spy.assert_not_called()
|
|
438
|
+
|
|
439
|
+
@staticmethod
|
|
440
|
+
def test_sync_after_update_config_uses_new_timestamp() -> None:
|
|
441
|
+
"""sync_from_config after update_config should display the refreshed timestamp."""
|
|
442
|
+
window = _make_window(_make_config(last_client_update=None))
|
|
443
|
+
assert window._last_client_update_label.text() == ''
|
|
444
|
+
|
|
445
|
+
new_config = _make_config(last_client_update='2026-03-09T12:00:00+00:00')
|
|
446
|
+
window.update_config(new_config)
|
|
447
|
+
window.sync_from_config()
|
|
448
|
+
|
|
449
|
+
assert 'Last updated:' in window._last_client_update_label.text()
|
{synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/tests/unit/qt/test_update_controller.py
RENAMED
|
@@ -414,3 +414,57 @@ class TestCheckError:
|
|
|
414
414
|
ctrl._on_check_error('timeout', silent=True)
|
|
415
415
|
|
|
416
416
|
assert banner.state.name == 'HIDDEN'
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
# ---------------------------------------------------------------------------
|
|
420
|
+
# Timestamp sync — _persist_check_timestamp updates config
|
|
421
|
+
# ---------------------------------------------------------------------------
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
class TestPersistCheckTimestamp:
|
|
425
|
+
"""Verify _persist_check_timestamp syncs the settings config."""
|
|
426
|
+
|
|
427
|
+
@staticmethod
|
|
428
|
+
def test_persist_updates_settings_config() -> None:
|
|
429
|
+
"""_persist_check_timestamp should call update_config on the settings window."""
|
|
430
|
+
ctrl, _app, _client, _banner, settings = _make_controller()
|
|
431
|
+
|
|
432
|
+
fake_resolved = _make_config(last_client_update='2026-03-09T00:00:00+00:00')
|
|
433
|
+
with patch(
|
|
434
|
+
'synodic_client.application.update_controller.update_user_config',
|
|
435
|
+
return_value=fake_resolved,
|
|
436
|
+
):
|
|
437
|
+
ctrl._persist_check_timestamp()
|
|
438
|
+
|
|
439
|
+
settings.update_config.assert_called_once_with(fake_resolved)
|
|
440
|
+
settings.set_last_checked.assert_called_once()
|
|
441
|
+
|
|
442
|
+
@staticmethod
|
|
443
|
+
def test_on_check_finished_success_syncs_config() -> None:
|
|
444
|
+
"""A successful check should persist timestamp AND sync settings config."""
|
|
445
|
+
ctrl, _app, _client, _banner, settings = _make_controller()
|
|
446
|
+
result = UpdateInfo(available=False, current_version=Version('1.0.0'))
|
|
447
|
+
|
|
448
|
+
fake_resolved = _make_config(last_client_update='2026-03-09T00:00:00+00:00')
|
|
449
|
+
with patch(
|
|
450
|
+
'synodic_client.application.update_controller.update_user_config',
|
|
451
|
+
return_value=fake_resolved,
|
|
452
|
+
):
|
|
453
|
+
ctrl._on_check_finished(result, silent=True)
|
|
454
|
+
|
|
455
|
+
settings.update_config.assert_called_once_with(fake_resolved)
|
|
456
|
+
|
|
457
|
+
@staticmethod
|
|
458
|
+
def test_download_finished_syncs_config_and_label() -> None:
|
|
459
|
+
"""_on_download_finished should sync config and update the label."""
|
|
460
|
+
ctrl, _app, _client, _banner, settings = _make_controller(auto_apply=False)
|
|
461
|
+
|
|
462
|
+
fake_resolved = _make_config(last_client_update='2026-03-09T00:00:00+00:00')
|
|
463
|
+
with patch(
|
|
464
|
+
'synodic_client.application.update_controller.update_user_config',
|
|
465
|
+
return_value=fake_resolved,
|
|
466
|
+
):
|
|
467
|
+
ctrl._on_download_finished(True, '2.0.0')
|
|
468
|
+
|
|
469
|
+
settings.update_config.assert_called_once_with(fake_resolved)
|
|
470
|
+
settings.set_last_checked.assert_called_once()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/synodic_client/application/__init__.py
RENAMED
|
File without changes
|
{synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/synodic_client/application/bootstrap.py
RENAMED
|
File without changes
|
{synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/synodic_client/application/data.py
RENAMED
|
File without changes
|
{synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/synodic_client/application/icon.py
RENAMED
|
File without changes
|
{synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/synodic_client/application/init.py
RENAMED
|
File without changes
|
{synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/synodic_client/application/instance.py
RENAMED
|
File without changes
|
|
File without changes
|
{synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/synodic_client/application/schema.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/synodic_client/application/screen/card.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/synodic_client/application/screen/tray.py
RENAMED
|
File without changes
|
|
File without changes
|
{synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/synodic_client/application/theme.py
RENAMED
|
File without changes
|
|
File without changes
|
{synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/synodic_client/application/workers.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/tests/unit/qt/test_gather_packages.py
RENAMED
|
File without changes
|
{synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/tests/unit/qt/test_install_preview.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/tests/unit/qt/test_preview_model.py
RENAMED
|
File without changes
|
|
File without changes
|
{synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/tests/unit/qt/test_tray_window_show.py
RENAMED
|
File without changes
|
{synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/tests/unit/qt/test_update_banner.py
RENAMED
|
File without changes
|
{synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/tests/unit/qt/test_update_feedback.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/tests/unit/windows/test_protocol.py
RENAMED
|
File without changes
|
{synodic_client-0.0.1.dev61 → synodic_client-0.0.1.dev62}/tests/unit/windows/test_startup.py
RENAMED
|
File without changes
|