mirror of
https://github.com/PR0M3TH3AN/SeedPass.git
synced 2025-09-08 23:38:49 +00:00
Guard missing Nostr client in background sync
This commit is contained in:
@@ -1401,6 +1401,8 @@ class PasswordManager:
|
|||||||
|
|
||||||
async def sync_index_from_nostr_async(self) -> None:
|
async def sync_index_from_nostr_async(self) -> None:
|
||||||
"""Always fetch the latest vault data from Nostr and update the local index."""
|
"""Always fetch the latest vault data from Nostr and update the local index."""
|
||||||
|
if not getattr(self, "nostr_client", None):
|
||||||
|
return
|
||||||
start = time.perf_counter()
|
start = time.perf_counter()
|
||||||
try:
|
try:
|
||||||
if getattr(self, "current_fingerprint", None):
|
if getattr(self, "current_fingerprint", None):
|
||||||
@@ -1518,6 +1520,8 @@ class PasswordManager:
|
|||||||
"""Launch a thread to synchronize the vault without blocking the UI."""
|
"""Launch a thread to synchronize the vault without blocking the UI."""
|
||||||
if getattr(self, "offline_mode", False):
|
if getattr(self, "offline_mode", False):
|
||||||
return
|
return
|
||||||
|
if getattr(self, "nostr_client", None) is None:
|
||||||
|
return
|
||||||
if getattr(self, "_sync_task", None) and not getattr(
|
if getattr(self, "_sync_task", None) and not getattr(
|
||||||
self._sync_task, "done", True
|
self._sync_task, "done", True
|
||||||
):
|
):
|
||||||
@@ -1529,7 +1533,7 @@ class PasswordManager:
|
|||||||
await self.sync_index_from_nostr_async()
|
await self.sync_index_from_nostr_async()
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logger.warning(f"Background sync failed: {exc}")
|
logger.warning(f"Background sync failed: {exc}")
|
||||||
if hasattr(self, "error_queue"):
|
if hasattr(self, "error_queue") and getattr(self, "nostr_client", None):
|
||||||
self.error_queue.put(exc)
|
self.error_queue.put(exc)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -1615,6 +1619,9 @@ class PasswordManager:
|
|||||||
local index file was written. Returns ``False`` otherwise. The local
|
local index file was written. Returns ``False`` otherwise. The local
|
||||||
index file is not created on failure.
|
index file is not created on failure.
|
||||||
"""
|
"""
|
||||||
|
if not getattr(self, "nostr_client", None):
|
||||||
|
return False
|
||||||
|
|
||||||
index_file = self.fingerprint_dir / "seedpass_entries_db.json.enc"
|
index_file = self.fingerprint_dir / "seedpass_entries_db.json.enc"
|
||||||
if index_file.exists():
|
if index_file.exists():
|
||||||
return True
|
return True
|
||||||
@@ -1680,6 +1687,8 @@ class PasswordManager:
|
|||||||
asyncio.run(self.sync_index_from_nostr_if_missing_async())
|
asyncio.run(self.sync_index_from_nostr_if_missing_async())
|
||||||
|
|
||||||
async def sync_index_from_nostr_if_missing_async(self) -> None:
|
async def sync_index_from_nostr_if_missing_async(self) -> None:
|
||||||
|
if not getattr(self, "nostr_client", None):
|
||||||
|
return
|
||||||
success = await self.attempt_initial_sync_async()
|
success = await self.attempt_initial_sync_async()
|
||||||
if not success:
|
if not success:
|
||||||
self.vault.save_index({"schema_version": LATEST_VERSION, "entries": {}})
|
self.vault.save_index({"schema_version": LATEST_VERSION, "entries": {}})
|
||||||
|
@@ -12,6 +12,7 @@ def _make_pm():
|
|||||||
pm.notify = lambda msg, level="INFO": pm.notifications.put(
|
pm.notify = lambda msg, level="INFO": pm.notifications.put(
|
||||||
manager_module.Notification(msg, level)
|
manager_module.Notification(msg, level)
|
||||||
)
|
)
|
||||||
|
pm.nostr_client = object()
|
||||||
return pm
|
return pm
|
||||||
|
|
||||||
|
|
||||||
|
@@ -1,10 +1,14 @@
|
|||||||
import sys
|
import sys
|
||||||
import importlib
|
import importlib
|
||||||
|
import queue
|
||||||
|
import time
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from tempfile import TemporaryDirectory
|
from tempfile import TemporaryDirectory
|
||||||
from types import SimpleNamespace
|
from types import SimpleNamespace
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
from helpers import create_vault, TEST_SEED, TEST_PASSWORD
|
from helpers import create_vault, TEST_SEED, TEST_PASSWORD
|
||||||
|
|
||||||
sys.path.append(str(Path(__file__).resolve().parents[1]))
|
sys.path.append(str(Path(__file__).resolve().parents[1]))
|
||||||
@@ -12,6 +16,7 @@ sys.path.append(str(Path(__file__).resolve().parents[1]))
|
|||||||
import main
|
import main
|
||||||
from nostr.client import DEFAULT_RELAYS
|
from nostr.client import DEFAULT_RELAYS
|
||||||
from seedpass.core.config_manager import ConfigManager
|
from seedpass.core.config_manager import ConfigManager
|
||||||
|
from seedpass.core.manager import Notification, PasswordManager
|
||||||
from seedpass.core.vault import Vault
|
from seedpass.core.vault import Vault
|
||||||
from utils.fingerprint_manager import FingerprintManager
|
from utils.fingerprint_manager import FingerprintManager
|
||||||
|
|
||||||
@@ -137,3 +142,31 @@ def test_settings_menu_change_password_incorrect(monkeypatch, capsys):
|
|||||||
|
|
||||||
out = capsys.readouterr().out
|
out = capsys.readouterr().out
|
||||||
assert "Incorrect password" in out
|
assert "Incorrect password" in out
|
||||||
|
|
||||||
|
|
||||||
|
def test_settings_menu_without_nostr_client(monkeypatch):
|
||||||
|
pm = PasswordManager.__new__(PasswordManager)
|
||||||
|
pm.offline_mode = False
|
||||||
|
pm.nostr_client = None
|
||||||
|
pm.notifications = queue.Queue()
|
||||||
|
pm.error_queue = queue.Queue()
|
||||||
|
pm.notify = lambda msg, level="INFO": pm.notifications.put(Notification(msg, level))
|
||||||
|
pm.is_dirty = False
|
||||||
|
pm.last_update = time.time()
|
||||||
|
pm.last_activity = time.time()
|
||||||
|
pm.update_activity = lambda: None
|
||||||
|
pm.lock_vault = lambda: None
|
||||||
|
pm.unlock_vault = lambda: None
|
||||||
|
pm.start_background_relay_check = lambda: None
|
||||||
|
pm.poll_background_errors = PasswordManager.poll_background_errors.__get__(pm)
|
||||||
|
pm.display_stats = lambda: None
|
||||||
|
|
||||||
|
inputs = iter(["7", ""])
|
||||||
|
monkeypatch.setattr(main, "timed_input", lambda *_: next(inputs))
|
||||||
|
monkeypatch.setattr("builtins.input", lambda *_: "")
|
||||||
|
|
||||||
|
with pytest.raises(SystemExit):
|
||||||
|
main.display_menu(pm, sync_interval=1000, inactivity_timeout=1000)
|
||||||
|
|
||||||
|
assert pm.error_queue.empty()
|
||||||
|
assert pm.notifications.empty()
|
||||||
|
@@ -17,6 +17,7 @@ def test_unlock_triggers_sync(monkeypatch, tmp_path):
|
|||||||
pm.setup_encryption_manager = lambda *a, **k: None
|
pm.setup_encryption_manager = lambda *a, **k: None
|
||||||
pm.initialize_bip85 = lambda: None
|
pm.initialize_bip85 = lambda: None
|
||||||
pm.initialize_managers = lambda: None
|
pm.initialize_managers = lambda: None
|
||||||
|
pm.nostr_client = object()
|
||||||
called = {"sync": False}
|
called = {"sync": False}
|
||||||
|
|
||||||
async def fake_sync(self):
|
async def fake_sync(self):
|
||||||
@@ -62,6 +63,7 @@ def test_quick_unlock_background_sync(monkeypatch, tmp_path):
|
|||||||
def test_start_background_sync_running_loop(monkeypatch):
|
def test_start_background_sync_running_loop(monkeypatch):
|
||||||
pm = PasswordManager.__new__(PasswordManager)
|
pm = PasswordManager.__new__(PasswordManager)
|
||||||
pm.offline_mode = False
|
pm.offline_mode = False
|
||||||
|
pm.nostr_client = object()
|
||||||
called = {"init": False, "sync": False}
|
called = {"init": False, "sync": False}
|
||||||
|
|
||||||
async def fake_attempt(self):
|
async def fake_attempt(self):
|
||||||
|
Reference in New Issue
Block a user