""" Tests for the history-save hook in ``BalWindow``. Verifies that every successful prepare (including the "Prepare" menu action) persists the freshly prepared transactions into the wallet's local history, while abort paths (which return ``None``) skip the save. Also verifies that after any local-history change (saving or removing will transactions) the wallet tabs are re-rendered through ``update_tabs`` (all tabs) plus ``update_status`` (status-bar balance), and that the rebuild path uses the same full refresh. Run: source electrum/env/bin/activate QT_QPA_PLATFORM=offscreen python3 tests/test_gui_prepare_will_history.py """ import sys sys.path.insert(0, __file__.rsplit("/", 2)[0]) from unittest.mock import Mock, patch import bal.gui.qt.window as win_mod from bal.core.util import Util from bal.core.will import NotCompleteWillException, Will from bal.gui.qt.window import BalWindow # ------------------------------------------------------------------ # # prepare_will -> _save_will_to_history # ------------------------------------------------------------------ # def test_prepare_will_saves_to_history_on_success(): win = object.__new__(BalWindow) will = {"wid": object()} with ( patch.object(BalWindow, "build_inheritance_transaction", return_value=will) as build_mock, patch.object(BalWindow, "_save_will_to_history") as save_mock, ): result = BalWindow.prepare_will(win) assert result is will build_mock.assert_called_once_with(ignore_duplicate=False, keep_original=False) save_mock.assert_called_once_with() def test_prepare_will_skips_save_when_build_aborted(): win = object.__new__(BalWindow) with ( patch.object(BalWindow, "build_inheritance_transaction", return_value=None) as build_mock, patch.object(BalWindow, "_save_will_to_history") as save_mock, ): result = BalWindow.prepare_will(win) assert result is None build_mock.assert_called_once_with(ignore_duplicate=False, keep_original=False) save_mock.assert_not_called() # ------------------------------------------------------------------ # # history refresh: _save_will_to_history -> update_tabs + update_status # ------------------------------------------------------------------ # class _Cfg: def __init__(self, value): self._value = value def get(self): return self._value class _CfgBag: def __init__(self, **kwargs): for name, value in kwargs.items(): setattr(self, name, value) class _Wallet: def dust_threshold(self): return 546 class _FakeWindow: def __init__(self, *, with_update_tabs=True): self.show_message = Mock() self.update_status = Mock() self.history_list = Mock() self.history_list.update = Mock() if with_update_tabs: self.update_tabs = Mock() class _FakeQTimer: calls = [] @classmethod def singleShot(cls, delay, callable_): cls.calls.append((delay, callable_)) def _make_save_window(save_enabled): win = object.__new__(BalWindow) win.bal_plugin = _CfgBag( SAVE_HISTORY=_Cfg(save_enabled), HISTORY_LABEL=_Cfg("LBL") ) win.willitems = {"wid": object()} win.wallet = object() return win def test_save_will_to_history_schedules_refresh_when_enabled(): win = _make_save_window(save_enabled=True) with ( patch.object(Will, "save_valid_transactions_to_history") as save_mock, patch.object(BalWindow, "_schedule_history_refresh") as schedule_mock, ): BalWindow._save_will_to_history(win) save_mock.assert_called_once_with(win.willitems, win.wallet, "LBL") schedule_mock.assert_called_once_with() def test_save_will_to_history_skips_save_and_refresh_when_disabled(): win = _make_save_window(save_enabled=False) with ( patch.object(Will, "save_valid_transactions_to_history") as save_mock, patch.object(BalWindow, "_schedule_history_refresh") as schedule_mock, ): BalWindow._save_will_to_history(win) save_mock.assert_not_called() schedule_mock.assert_not_called() def test_save_will_to_history_schedules_refresh_even_on_error(): win = _make_save_window(save_enabled=True) with ( patch.object( Will, "save_valid_transactions_to_history", side_effect=RuntimeError("boom"), ), patch.object(BalWindow, "_schedule_history_refresh") as schedule_mock, ): BalWindow._save_will_to_history(win) schedule_mock.assert_called_once_with() def test_schedule_history_refresh_marshals_to_gui_thread(): win = object.__new__(BalWindow) with patch.object(win_mod, "QTimer", _FakeQTimer): _FakeQTimer.calls.clear() BalWindow._schedule_history_refresh(win) assert _FakeQTimer.calls == [(0, win._refresh_after_history_save)] def test_refresh_after_history_save_calls_update_tabs_and_status(): win = object.__new__(BalWindow) win.window = _FakeWindow(with_update_tabs=True) BalWindow._refresh_after_history_save(win) win.window.update_tabs.assert_called_once_with() win.window.update_status.assert_called_once_with() win.window.history_list.update.assert_not_called() def test_refresh_after_history_save_falls_back_to_history_list(): win = object.__new__(BalWindow) win.window = _FakeWindow(with_update_tabs=False) BalWindow._refresh_after_history_save(win) win.window.history_list.update.assert_called_once_with() win.window.update_status.assert_called_once_with() def test_rebuild_path_schedules_full_refresh(): win = object.__new__(BalWindow) win.disable_plugin = False win.heirs = {"h": object()} win.willexecutors = {} win.no_willexecutor = True win.willitems = {} win.will = {} win.date_to_check = 1_800_000_000 win.will_settings = {"baltx_fees": 1, "locktime": "1 month"} win.bal_plugin = _CfgBag( MAX_WILLEXECUTOR_FEE=_Cfg(1), SAVE_HISTORY=_Cfg(True), HISTORY_LABEL=_Cfg("LBL"), ) win.window = _FakeWindow() win.window.wallet = _Wallet() with ( patch.object(Util, "get_available_utxos", return_value=[]), patch.object(Util, "parse_locktime_string", return_value=1_800_000_001), patch.object(Will, "get_min_locktime", return_value=0), patch.object(Will, "check_amounts"), patch.object(BalWindow, "init_class_variables"), patch.object(BalWindow, "build_will"), patch.object( BalWindow, "check_will", side_effect=[NotCompleteWillException(), None], ), patch.object(BalWindow, "update_all"), patch.object(BalWindow, "_schedule_history_refresh") as schedule_mock, ): BalWindow.build_inheritance_transaction(win) schedule_mock.assert_called_once_with() # ------------------------------------------------------------------ # # Main # ------------------------------------------------------------------ # if __name__ == "__main__": for name in sorted(dir()): if name.startswith("test_"): globals()[name]() print(f" [OK] {name}") print("[OK] All prepare-will history tests passed")