Owner-approved changes after testing v0.4.6, plus accumulated v0.4.x work, task-tracking notes, and an updated project HANDOFF document. The four v0.4.7 changes: 1. Report area (BalBuildWillDialog) opens 500px tall (min) up to 700px (max), then the scrollbar takes over. Previously it opened ~140px (too short). 2. Heirs are listed ONE per line again (green, bold) in _build_success_report, reverting the v0.4.6 single-line form. Heir names can be long and the report now scrolls, so compression is no longer needed. 3. Two wizard texts get an explicit line break: after "(or backup)" in the date hint and after "miner fees" in the fee note (widgets.py). 4. ALL-DUST guard: when EVERY heir's share is below the Bitcoin dust limit, the inheritance would pay nobody. Heirs.prepare_lists now raises HeirAmountIsDustException at the end (where all heirs across all locktimes are known with their final dust state), and dialogs.task_phase1 shows a clear RED message and stops without building/signing/checking. A mix of dust + valid heirs keeps building normally. The guard is intentionally in prepare_lists, NOT prepare_transactions (which only sees the lowest locktime and would false-positive). HeirAmountIsDustException is imported in common.py. Tests: 3 new tests in test_core_heirs_extra.py pin the dust behaviour (all-dust raises; mixed continues; multi-locktime continues). Full suite: 258 passed. ruff: no new errors. Version bumped 0.4.6 -> 0.4.7 (4 files). CHANGELOG #23. Also adds/updates HANDOFF.md so any future AI (Claude or another model) can resume the project with full context (rules, layout, build/test/lint, dust logic, git flow), and records the task-tracking notes in .agent_memory_tasks.md.
117 lines
4.1 KiB
Python
117 lines
4.1 KiB
Python
"""
|
|
Tests for Group F (heir-change full rebuild, "Option A").
|
|
|
|
Context (bugs E/F/K): when an heir was deleted/changed and the rebuilt
|
|
inheritance transaction happened to keep the SAME txid, ``Will.update_will``
|
|
used to REUSE the old (already signed/COMPLETE) WillItem, copying only the new
|
|
heirs onto it. The downstream ``have_to_sign`` check then saw the item as
|
|
COMPLETE and reported "Nothing to do", so the new will was never signed or
|
|
broadcast.
|
|
|
|
The fix reuses the old item ONLY when the real heirs are identical. These tests
|
|
pin the behaviour of the helper ``Will._same_heirs`` that drives that decision.
|
|
|
|
Run:
|
|
source electrum/env/bin/activate
|
|
python3 tests/test_group_f_heir_change_rebuild.py
|
|
"""
|
|
|
|
import sys
|
|
import os
|
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir))
|
|
|
|
from bal.core.will import Will
|
|
|
|
|
|
# Heir entry layout (see heirs.py): [0]=address, [1]=amount, [2]=locktime.
|
|
# Extra trailing fields (e.g. real/dust amount) must NOT affect equality.
|
|
def _heir(address, amount, locktime, *extra):
|
|
return [address, amount, locktime, *extra]
|
|
|
|
|
|
def test_same_heirs_identical():
|
|
"""Identical real heirs -> True (old signed item may be reused)."""
|
|
a = {"alice": _heir("bc1qalice", "50%", "2026-12-01")}
|
|
b = {"alice": _heir("bc1qalice", "50%", "2026-12-01")}
|
|
assert Will._same_heirs(a, b) is True
|
|
|
|
|
|
def test_same_heirs_ignores_extra_fields():
|
|
"""Derived/extra fields (real amount, dust flag) do not break equality."""
|
|
a = {"alice": _heir("bc1qalice", "50%", "2026-12-01", 12345, "DUST")}
|
|
b = {"alice": _heir("bc1qalice", "50%", "2026-12-01")}
|
|
assert Will._same_heirs(a, b) is True
|
|
|
|
|
|
def test_same_heirs_deleted_heir():
|
|
"""Deleting one of two heirs -> changed -> False (must rebuild/re-sign)."""
|
|
old = {
|
|
"alice": _heir("bc1qalice", "50%", "2026-12-01"),
|
|
"bob": _heir("bc1qbob", "50%", "2026-12-01"),
|
|
}
|
|
new = {
|
|
# bob removed; alice is auto-scaled to 100% by the plugin.
|
|
"alice": _heir("bc1qalice", "100%", "2026-12-01"),
|
|
}
|
|
assert Will._same_heirs(old, new) is False
|
|
|
|
|
|
def test_same_heirs_added_heir():
|
|
"""Adding a heir -> changed -> False."""
|
|
old = {"alice": _heir("bc1qalice", "100%", "2026-12-01")}
|
|
new = {
|
|
"alice": _heir("bc1qalice", "50%", "2026-12-01"),
|
|
"carol": _heir("bc1qcarol", "50%", "2026-12-01"),
|
|
}
|
|
assert Will._same_heirs(old, new) is False
|
|
|
|
|
|
def test_same_heirs_changed_amount():
|
|
"""Same heir name but different amount -> changed -> False."""
|
|
old = {"alice": _heir("bc1qalice", "50%", "2026-12-01")}
|
|
new = {"alice": _heir("bc1qalice", "70%", "2026-12-01")}
|
|
assert Will._same_heirs(old, new) is False
|
|
|
|
|
|
def test_same_heirs_changed_address():
|
|
"""Same heir name but different destination address -> False."""
|
|
old = {"alice": _heir("bc1qalice", "50%", "2026-12-01")}
|
|
new = {"alice": _heir("bc1qOTHER", "50%", "2026-12-01")}
|
|
assert Will._same_heirs(old, new) is False
|
|
|
|
|
|
def test_same_heirs_changed_locktime():
|
|
"""Same heir but different delivery locktime -> False."""
|
|
old = {"alice": _heir("bc1qalice", "50%", "2026-12-01")}
|
|
new = {"alice": _heir("bc1qalice", "50%", "2027-06-01")}
|
|
assert Will._same_heirs(old, new) is False
|
|
|
|
|
|
def test_same_heirs_ignores_willexecutor_pseudo_heirs():
|
|
"""Reserved 'w!ll3x3c\"' pseudo-heirs are bookkeeping, not real heirs.
|
|
|
|
A difference only in the pseudo-heir entries must NOT be reported as a heir
|
|
change (the will-executor is refreshed separately in update_will).
|
|
"""
|
|
old = {
|
|
"alice": _heir("bc1qalice", "100%", "2026-12-01"),
|
|
'w!ll3x3c"server1': _heir("bc1qwe1", "0", "0"),
|
|
}
|
|
new = {
|
|
"alice": _heir("bc1qalice", "100%", "2026-12-01"),
|
|
'w!ll3x3c"server2': _heir("bc1qwe2", "0", "0"),
|
|
}
|
|
assert Will._same_heirs(old, new) is True
|
|
|
|
|
|
def test_same_heirs_empty():
|
|
"""Two empty heir maps are equal; None is treated as empty."""
|
|
assert Will._same_heirs({}, {}) is True
|
|
assert Will._same_heirs(None, {}) is True
|
|
assert Will._same_heirs(None, None) is True
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import pytest
|
|
raise SystemExit(pytest.main([__file__, "-v"]))
|