150 lines
7.1 KiB
Python
150 lines
7.1 KiB
Python
"""
|
|
Test: BAL plugin CLI commands are registered with Electrum.
|
|
|
|
Verifies that importing the plugin through Electrum's own plugin loader
|
|
(``Plugins(config, cmd_only=True)``, the exact code path ``run_electrum`` uses
|
|
to pre-parse the command line) registers every ``bal_*`` command with
|
|
``electrum.commands`` (``known_commands`` + the ``Commands`` class).
|
|
|
|
It also asserts the basic contract enforced by ``plugin_command``: each command
|
|
is a coroutine and carries the expected flags (all ``bal_*`` commands require a
|
|
daemon/network, i.e. the ``'n'`` flag; the wallet-bound ones the ``'w'`` flag;
|
|
signing also ``'p'``).
|
|
|
|
Run:
|
|
source /home/steal/devel/bal/electrum/env/bin/activate
|
|
python3 tests/test_cli_commands_registered.py
|
|
"""
|
|
|
|
import inspect
|
|
import tempfile
|
|
|
|
from electrum import commands as electrum_commands
|
|
from electrum.plugin import Plugins
|
|
from electrum.simple_config import SimpleConfig
|
|
|
|
# The full command table lives in PLAN_CMDLINE_PLUGIN.md section 6; new commands
|
|
# added in later phases must be appended here so the registration test keeps
|
|
# proving the whole list is wired up.
|
|
EXPECTED_COMMANDS = {
|
|
# Settings (no wallet required)
|
|
"bal_settings_list": {
|
|
"requires_network": True,
|
|
"requires_wallet": False,
|
|
"requires_password": False,
|
|
},
|
|
"bal_settings_get": {
|
|
"requires_network": True,
|
|
"requires_wallet": False,
|
|
"requires_password": False,
|
|
},
|
|
"bal_settings_set": {
|
|
"requires_network": True,
|
|
"requires_wallet": False,
|
|
"requires_password": False,
|
|
},
|
|
"bal_settings_reset": {
|
|
"requires_network": True,
|
|
"requires_wallet": False,
|
|
"requires_password": False,
|
|
},
|
|
# Heirs
|
|
"bal_heirs_list": {"requires_network": True, "requires_wallet": True, "requires_password": False},
|
|
"bal_heirs_show": {"requires_network": True, "requires_wallet": True, "requires_password": False},
|
|
"bal_heirs_add": {"requires_network": True, "requires_wallet": True, "requires_password": False},
|
|
"bal_heirs_update": {"requires_network": True, "requires_wallet": True, "requires_password": False},
|
|
"bal_heirs_delete": {"requires_network": True, "requires_wallet": True, "requires_password": False},
|
|
"bal_heirs_import": {"requires_network": True, "requires_wallet": True, "requires_password": False},
|
|
"bal_heirs_export": {"requires_network": True, "requires_wallet": True, "requires_password": False},
|
|
# Will-Executors
|
|
"bal_willexecutors_list": {"requires_network": True, "requires_wallet": True, "requires_password": False},
|
|
"bal_willexecutors_show": {"requires_network": True, "requires_wallet": True, "requires_password": False},
|
|
"bal_willexecutors_add": {"requires_network": True, "requires_wallet": True, "requires_password": False},
|
|
"bal_willexecutors_update": {"requires_network": True, "requires_wallet": True, "requires_password": False},
|
|
"bal_willexecutors_select": {"requires_network": True, "requires_wallet": True, "requires_password": False},
|
|
"bal_willexecutors_delete": {"requires_network": True, "requires_wallet": True, "requires_password": False},
|
|
"bal_willexecutors_ping": {"requires_network": True, "requires_wallet": True, "requires_password": False},
|
|
"bal_willexecutors_download": {"requires_network": True, "requires_wallet": True, "requires_password": False},
|
|
"bal_willexecutors_import": {"requires_network": True, "requires_wallet": True, "requires_password": False},
|
|
"bal_willexecutors_export": {"requires_network": True, "requires_wallet": True, "requires_password": False},
|
|
# Will
|
|
"bal_will_status": {"requires_network": True, "requires_wallet": True, "requires_password": False},
|
|
"bal_will_check": {"requires_network": True, "requires_wallet": True, "requires_password": False},
|
|
"bal_will_prepare": {"requires_network": True, "requires_wallet": True, "requires_password": False},
|
|
"bal_will_sign": {"requires_network": True, "requires_wallet": True, "requires_password": True},
|
|
"bal_will_broadcast": {"requires_network": True, "requires_wallet": True, "requires_password": False},
|
|
"bal_will_export": {"requires_network": True, "requires_wallet": True, "requires_password": False},
|
|
"bal_will_import_merge": {"requires_network": True, "requires_wallet": True, "requires_password": False},
|
|
"bal_will_invalidate": {"requires_network": True, "requires_wallet": True, "requires_password": False},
|
|
"bal_will_check_executor": {"requires_network": True, "requires_wallet": True, "requires_password": False},
|
|
}
|
|
|
|
|
|
def _isolated_config(**overrides):
|
|
"""A throwaway SimpleConfig that never touches the real Electrum config.
|
|
|
|
A fresh ``electrum_path`` temp dir keeps every write isolated, so running
|
|
the tests cannot pollute the user's config files. The bal plugin is
|
|
enabled because ``Plugins(cmd_only=True)`` skips any plugin that is not
|
|
explicitly enabled (electrum.plugin.Plugins.find_directory_plugins).
|
|
"""
|
|
opts = {"electrum_path": tempfile.mkdtemp(prefix="bal_test_")}
|
|
opts.update(overrides)
|
|
cfg = SimpleConfig(opts)
|
|
cfg.enable_plugin("bal")
|
|
return cfg
|
|
|
|
|
|
def test_commands_registered():
|
|
cfg = _isolated_config()
|
|
Plugins(cfg, cmd_only=True)
|
|
for name, flags in EXPECTED_COMMANDS.items():
|
|
assert name in electrum_commands.known_commands, f"{name} not registered"
|
|
cmd = electrum_commands.known_commands[name]
|
|
assert cmd.name == name
|
|
assert cmd.requires_network is flags["requires_network"]
|
|
assert cmd.requires_wallet is flags["requires_wallet"]
|
|
assert cmd.requires_password is flags["requires_password"]
|
|
|
|
|
|
def test_commands_are_coroutines():
|
|
cfg = _isolated_config()
|
|
Plugins(cfg, cmd_only=True)
|
|
for name in EXPECTED_COMMANDS:
|
|
func = getattr(electrum_commands.Commands, name, None)
|
|
assert func is not None, f"{name} missing from Commands"
|
|
assert inspect.iscoroutinefunction(func), f"{name} is not a coroutine"
|
|
|
|
|
|
def test_no_duplicate_registration():
|
|
"""Loading the plugin twice must not raise "Command name bal_... already
|
|
exists" (the guard in bal/__init__._register_cli_commands)."""
|
|
cfg = _isolated_config()
|
|
plugins = Plugins(cfg, cmd_only=True)
|
|
plugins.maybe_load_plugin_init_method("bal") # already imported -> no-op
|
|
for name in EXPECTED_COMMANDS:
|
|
assert name in electrum_commands.known_commands
|
|
|
|
|
|
def test_command_docstrings_document_all_args():
|
|
"""Every parameter/option must carry an ``arg:TYPE:NAME:DESC`` line (the
|
|
CLI parser prints "undocumented argument ..." otherwise)."""
|
|
cfg = _isolated_config()
|
|
Plugins(cfg, cmd_only=True)
|
|
for name in EXPECTED_COMMANDS:
|
|
cmd = electrum_commands.known_commands[name]
|
|
for varname in list(cmd.params) + list(cmd.options):
|
|
if varname in ("wallet", "wallet_path", "plugin", "password"):
|
|
continue
|
|
assert varname in cmd.arg_descriptions, (
|
|
f"{name}: undocumented argument {varname}"
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
for name in sorted(dir()):
|
|
if name.startswith("test_"):
|
|
globals()[name]()
|
|
print(f" [OK] {name}")
|
|
print("[OK] All CLI registration tests passed")
|