forked from bitcoinafterlife/bal-electrum-plugin
add tests
This commit is contained in:
171
tests/sim_update_flows.py
Normal file
171
tests/sim_update_flows.py
Normal file
@@ -0,0 +1,171 @@
|
||||
"""
|
||||
Real-world simulation of the inheritance update flows.
|
||||
|
||||
This script does NOT touch the GUI. It drives the core decision function
|
||||
``Will.check_willexecutors_and_heirs`` (the one that decides whether a will is
|
||||
still coherent or must be rebuilt) through the scenarios the user reported:
|
||||
|
||||
1. delivery date moved forward (postpone) -> must NOT stay "coherent"
|
||||
2. an heir is added -> must trigger rebuild
|
||||
3. an heir is removed -> must trigger rebuild
|
||||
4. a single heir percentage / amount is changed -> must trigger rebuild
|
||||
5. nothing changed -> stays coherent
|
||||
|
||||
For each scenario we report which exception (if any) is raised, because that is
|
||||
exactly what the GUI relies on to decide whether to rebuild the inheritance
|
||||
transactions. If the function returns True (coherent) when something DID
|
||||
change, the GUI will (correctly) show no update -- which is the symptom the
|
||||
user described.
|
||||
|
||||
Run:
|
||||
QT_QPA_PLATFORM=offscreen PYTHONPATH=electrum-src python3 tests/sim_update_flows.py
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import copy
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir))
|
||||
|
||||
from bal.core.will import (
|
||||
WillItem, Will,
|
||||
NotCompleteWillException, HeirNotFoundException, NoHeirsException,
|
||||
TxFeesChangedException, WillExpiredException,
|
||||
)
|
||||
from bal.core.util import Util
|
||||
|
||||
# A valid serialized tx (1 input + 1 output, version 2). Its nLockTime is 0.
|
||||
_VALID_TX_HEX = (
|
||||
"01000000012a5c9a94fcde98f5581cd00162c60a13936ceb75389ea65b"
|
||||
"f38633b424eb4031000000006c493046022100a82bbc57a0136751e543"
|
||||
"3f41cf000b3f1a99c6744775e76ec764fb78c54ee100022100f9e80b7d"
|
||||
"e89de861dc6fb0c1429d5da72c2b6b2ee2406bc9bfb1beedd729d98501"
|
||||
"2102e61d176da16edd1d258a200ad9759ef63adf8e14cd97f53227bae3"
|
||||
"5cdb84d2f6ffffffff0140420f00000000001976a914230ac37834073a"
|
||||
"42146f11ef8414ae929feaafc388ac00000000"
|
||||
)
|
||||
|
||||
# A locktime far in the past (so the frozen tx.locktime is a fixed integer we
|
||||
# control via monkey-patching below). We will override w.tx.locktime per test.
|
||||
TX_FEES = 100
|
||||
|
||||
|
||||
def _make_will_item(heirs, tx_locktime, status_complete=False):
|
||||
"""Build a WillItem whose stored heirs == ``heirs`` and whose tx.locktime
|
||||
is forced to ``tx_locktime`` (the value frozen in the signed Bitcoin tx)."""
|
||||
d = {
|
||||
"tx": _VALID_TX_HEX,
|
||||
"heirs": copy.deepcopy(heirs),
|
||||
"willexecutor": None,
|
||||
"status": "",
|
||||
"description": "",
|
||||
"time": 0,
|
||||
"change": "",
|
||||
"baltx_fees": TX_FEES,
|
||||
}
|
||||
item = WillItem(d, _id="willid_1")
|
||||
item.STATUS = copy.deepcopy(WillItem.STATUS_DEFAULT)
|
||||
# Force the locktime frozen "inside" the signed tx.
|
||||
item.tx.locktime = tx_locktime
|
||||
if status_complete:
|
||||
item.set_status("COMPLETE", True)
|
||||
return item
|
||||
|
||||
|
||||
def _run(label, will_heirs, current_heirs, tx_locktime,
|
||||
status_complete=False, check_date=0):
|
||||
"""Run check_willexecutors_and_heirs and report the outcome."""
|
||||
item = _make_will_item(will_heirs, tx_locktime, status_complete)
|
||||
will = {"willid_1": item}
|
||||
outcome = None
|
||||
try:
|
||||
result = Will.check_willexecutors_and_heirs(
|
||||
will,
|
||||
current_heirs, # the (possibly edited) heirs dict
|
||||
{}, # willexecutors
|
||||
False, # self_willexecutor
|
||||
check_date, # check_date (timestamp)
|
||||
TX_FEES, # tx_fees
|
||||
)
|
||||
outcome = f"coherent (returned {result})"
|
||||
except HeirNotFoundException as e:
|
||||
outcome = f"HeirNotFoundException: {e}"
|
||||
except NoHeirsException as e:
|
||||
outcome = f"NoHeirsException: {e}"
|
||||
except TxFeesChangedException as e:
|
||||
outcome = f"TxFeesChangedException: {e}"
|
||||
except WillExpiredException as e:
|
||||
outcome = f"WillExpiredException: {e}"
|
||||
except NotCompleteWillException as e:
|
||||
outcome = f"{type(e).__name__}: {e}"
|
||||
except Exception as e:
|
||||
outcome = f"!! UNEXPECTED {type(e).__name__}: {e}"
|
||||
print(f"[{label}]")
|
||||
print(f" -> {outcome}")
|
||||
return outcome
|
||||
|
||||
|
||||
def main():
|
||||
# locktime string "0d" -> Util.parse_locktime_string returns a timestamp
|
||||
# ~ now. We use explicit integer timestamps to keep things deterministic.
|
||||
base_lt = 1900000000 # frozen tx.locktime (year ~2030)
|
||||
later_lt = "2000000000" # a later locktime string (postpone)
|
||||
same_lt = str(base_lt)
|
||||
|
||||
# Scenario 0: nothing changed -> should be coherent.
|
||||
heirs = {"alice": ["addr_alice", 5000, same_lt]}
|
||||
_run("0. nothing changed",
|
||||
will_heirs=heirs, current_heirs=copy.deepcopy(heirs),
|
||||
tx_locktime=base_lt, check_date=0)
|
||||
|
||||
# Scenario 1: delivery date moved forward (postpone), will NOT yet signed.
|
||||
heirs_will = {"alice": ["addr_alice", 5000, same_lt]}
|
||||
heirs_now = {"alice": ["addr_alice", 5000, later_lt]}
|
||||
_run("1. date postponed (unsigned will)",
|
||||
will_heirs=heirs_will, current_heirs=heirs_now,
|
||||
tx_locktime=base_lt, check_date=0)
|
||||
|
||||
# Scenario 1b: postpone on a SIGNED will (status COMPLETE).
|
||||
_run("1b. date postponed (SIGNED will)",
|
||||
will_heirs=heirs_will, current_heirs=heirs_now,
|
||||
tx_locktime=base_lt, status_complete=True, check_date=0)
|
||||
|
||||
# Scenario 2: an heir is ADDED.
|
||||
heirs_will = {"alice": ["addr_alice", 5000, same_lt]}
|
||||
heirs_now = {
|
||||
"alice": ["addr_alice", 5000, same_lt],
|
||||
"bob": ["addr_bob", 3000, same_lt],
|
||||
}
|
||||
_run("2. heir added (bob)",
|
||||
will_heirs=heirs_will, current_heirs=heirs_now,
|
||||
tx_locktime=base_lt, check_date=0)
|
||||
|
||||
# Scenario 3: an heir is REMOVED.
|
||||
heirs_will = {
|
||||
"alice": ["addr_alice", 5000, same_lt],
|
||||
"bob": ["addr_bob", 3000, same_lt],
|
||||
}
|
||||
heirs_now = {"alice": ["addr_alice", 5000, same_lt]}
|
||||
_run("3. heir removed (bob)",
|
||||
will_heirs=heirs_will, current_heirs=heirs_now,
|
||||
tx_locktime=base_lt, check_date=0)
|
||||
|
||||
# Scenario 4: a single heir AMOUNT/percentage changed.
|
||||
heirs_will = {"alice": ["addr_alice", 5000, same_lt]}
|
||||
heirs_now = {"alice": ["addr_alice", 9999, same_lt]}
|
||||
_run("4. heir amount changed (5000 -> 9999)",
|
||||
will_heirs=heirs_will, current_heirs=heirs_now,
|
||||
tx_locktime=base_lt, check_date=0)
|
||||
|
||||
# Scenario 5: heir ADDRESS changed.
|
||||
heirs_will = {"alice": ["addr_alice", 5000, same_lt]}
|
||||
heirs_now = {"alice": ["addr_NEW", 5000, same_lt]}
|
||||
_run("5. heir address changed",
|
||||
will_heirs=heirs_will, current_heirs=heirs_now,
|
||||
tx_locktime=base_lt, check_date=0)
|
||||
|
||||
print("\n[done] simulation finished")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user