forked from bitcoinafterlife/bal-electrum-plugin
add tests
This commit is contained in:
259
tests/test_core_plugin_base.py
Normal file
259
tests/test_core_plugin_base.py
Normal file
@@ -0,0 +1,259 @@
|
||||
"""
|
||||
Comprehensive tests for ``bal.core.plugin_base``.
|
||||
|
||||
Covers BalTimestamp, BalConfig, and BalPlugin static helpers.
|
||||
|
||||
Run:
|
||||
source electrum/env/bin/activate
|
||||
python3 tests/test_core_plugin_base.py
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir))
|
||||
|
||||
from datetime import datetime, date, timedelta
|
||||
from bal.core.plugin_base import BalTimestamp, BalPlugin, BalConfig
|
||||
|
||||
|
||||
# ------------------------------------------------------------------ #
|
||||
# BalTimestamp
|
||||
# ------------------------------------------------------------------ #
|
||||
|
||||
def test_bt_create_and_str():
|
||||
bt = BalTimestamp("30d")
|
||||
assert bt.unit == "d"
|
||||
assert bt.value == 30
|
||||
|
||||
bt2 = BalTimestamp("1y")
|
||||
assert bt2.unit == "y"
|
||||
assert bt2.value == 1
|
||||
|
||||
bt3 = BalTimestamp(1700000000)
|
||||
assert bt3.unit is None
|
||||
assert bt3.value == 1700000000
|
||||
|
||||
bt4 = BalTimestamp("garbage")
|
||||
# fallback: value=1, unit=None
|
||||
assert bt4.value == 1
|
||||
assert bt4.unit is None
|
||||
|
||||
bt5 = BalTimestamp(0)
|
||||
assert bt5.unit is None
|
||||
assert bt5.value == 0
|
||||
|
||||
bt6 = BalTimestamp("7d")
|
||||
assert str(bt6) == "7d"
|
||||
|
||||
bt7 = BalTimestamp("2y")
|
||||
assert str(bt7) == "2y"
|
||||
|
||||
# absolute timestamp str -> ISO format
|
||||
bt8 = BalTimestamp(1700000000)
|
||||
s = str(bt8)
|
||||
assert "202" in s or "197" in s # year present
|
||||
|
||||
|
||||
def test_bt_duration_to_days():
|
||||
assert BalTimestamp("30d").duration_to_days() == 30
|
||||
assert BalTimestamp("1y").duration_to_days() == 365
|
||||
assert BalTimestamp("0d").duration_to_days() == 0
|
||||
assert BalTimestamp(1700000000).duration_to_days() == 1700000000 # unit None -> raw value
|
||||
|
||||
|
||||
def test_bt_to_date_absolute():
|
||||
bt = BalTimestamp(1700000000)
|
||||
d = bt.to_date()
|
||||
assert isinstance(d, datetime)
|
||||
|
||||
# absolute with from_date (should be ignored for absolute)
|
||||
d2 = bt.to_date(from_date=datetime(2020, 1, 1))
|
||||
assert d == d2
|
||||
|
||||
|
||||
def test_bt_to_date_relative():
|
||||
now = datetime.now()
|
||||
|
||||
# relative days from now
|
||||
bt = BalTimestamp("7d")
|
||||
d = bt.to_date()
|
||||
assert d.hour == 0 and d.minute == 0 # normalized to midnight
|
||||
assert d > now
|
||||
|
||||
# reverse (subtract days)
|
||||
d_rev = bt.to_date(reverse=True)
|
||||
assert d_rev < now
|
||||
|
||||
# from explicit datetime
|
||||
base = datetime(2025, 6, 1, 12, 0, 0)
|
||||
d = bt.to_date(from_date=base)
|
||||
expected = (base + timedelta(days=7)).replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
assert d == expected
|
||||
|
||||
# from int timestamp
|
||||
ts = int(base.timestamp())
|
||||
d = bt.to_date(from_date=ts)
|
||||
assert d == expected
|
||||
|
||||
|
||||
def test_bt_to_date_years():
|
||||
bt = BalTimestamp("1y")
|
||||
d = bt.to_date()
|
||||
assert d > datetime.now()
|
||||
|
||||
|
||||
def test_bt_to_date_overflow():
|
||||
"""Huge relative durations should not crash (clamp to INT32_MAX)."""
|
||||
bt = BalTimestamp("999999999d")
|
||||
d = bt.to_date()
|
||||
# should not raise
|
||||
assert d is not None
|
||||
assert isinstance(d, datetime)
|
||||
|
||||
|
||||
def test_bt_to_timestamp():
|
||||
bt = BalTimestamp("7d")
|
||||
ts = bt.to_timestamp()
|
||||
assert ts > time.time()
|
||||
assert isinstance(ts, float)
|
||||
|
||||
bt2 = BalTimestamp(1700000000)
|
||||
assert abs(bt2.to_timestamp() - 1700000000) < 86400 # close to original
|
||||
|
||||
|
||||
def test_bt_repr():
|
||||
assert repr(BalTimestamp("7d")) == "7d"
|
||||
r = repr(BalTimestamp(1700000000))
|
||||
assert isinstance(r, str)
|
||||
assert len(r) > 0
|
||||
|
||||
|
||||
def test_bt_edge_values():
|
||||
# zero timestamp
|
||||
bt0 = BalTimestamp(0)
|
||||
d = bt0.to_date()
|
||||
assert d is not None
|
||||
|
||||
# negative? (may depend on platform)
|
||||
try:
|
||||
bt_neg = BalTimestamp(-1)
|
||||
_ = bt_neg.to_date()
|
||||
except (OSError, ValueError, OverflowError):
|
||||
pass # acceptable on some platforms
|
||||
|
||||
|
||||
# ------------------------------------------------------------------ #
|
||||
# BalTimestamp._safe_fromtimestamp
|
||||
# ------------------------------------------------------------------ #
|
||||
|
||||
def test_safe_fromtimestamp_normal():
|
||||
d = BalTimestamp._safe_fromtimestamp(1700000000)
|
||||
assert isinstance(d, datetime)
|
||||
|
||||
|
||||
def test_safe_fromtimestamp_nlocktime_max():
|
||||
"""NLOCKTIME_MAX (2**32-1) must not raise even on 32-bit platforms."""
|
||||
d = BalTimestamp._safe_fromtimestamp(2**32 - 1)
|
||||
assert d is not None
|
||||
|
||||
|
||||
def test_safe_fromtimestamp_negative():
|
||||
"""Negative timestamps should not crash."""
|
||||
d = BalTimestamp._safe_fromtimestamp(-1)
|
||||
assert isinstance(d, datetime)
|
||||
|
||||
|
||||
# ------------------------------------------------------------------ #
|
||||
# BalConfig
|
||||
# ------------------------------------------------------------------ #
|
||||
|
||||
class FakeConfig:
|
||||
"""Minimal mock for Electrum config."""
|
||||
def __init__(self):
|
||||
self._store = {}
|
||||
def get(self, key, default=None):
|
||||
return self._store.get(key, default)
|
||||
def set_key(self, key, value, save=True):
|
||||
self._store[key] = value
|
||||
|
||||
|
||||
def test_balconfig_default():
|
||||
cfg = FakeConfig()
|
||||
bc = BalConfig(cfg, "test_key", "default_val")
|
||||
assert bc.get() == "default_val"
|
||||
assert bc.get("override") == "override"
|
||||
assert bc.get(None) == "default_val"
|
||||
|
||||
|
||||
def test_balconfig_set():
|
||||
cfg = FakeConfig()
|
||||
bc = BalConfig(cfg, "test_key", "default_val")
|
||||
bc.set("stored_val")
|
||||
assert cfg.get("test_key") == "stored_val"
|
||||
assert bc.get() == "stored_val"
|
||||
|
||||
|
||||
# ------------------------------------------------------------------ #
|
||||
# BalPlugin
|
||||
# ------------------------------------------------------------------ #
|
||||
|
||||
def test_default_will_settings_relative():
|
||||
rel = BalPlugin.default_will_settings_relative()
|
||||
assert rel["threshold"] == "30d"
|
||||
assert rel["locktime"] == "1y"
|
||||
|
||||
|
||||
def test_default_will_settings():
|
||||
settings = BalPlugin.default_will_settings()
|
||||
assert settings["baltx_fees"] == 100
|
||||
assert "threshold" in settings
|
||||
assert "locktime" in settings
|
||||
# threshold/locktime should be absolute timestamps
|
||||
assert isinstance(settings["threshold"], float)
|
||||
assert isinstance(settings["locktime"], float)
|
||||
|
||||
|
||||
def test_default_will_settings_absolute():
|
||||
abs_ = BalPlugin.default_will_settings_absolute()
|
||||
assert "threshold" in abs_
|
||||
assert "locktime" in abs_
|
||||
# should be timestamps (in the future)
|
||||
today = datetime.combine(date.today(), datetime.min.time())
|
||||
assert abs_["threshold"] >= today.timestamp()
|
||||
assert abs_["locktime"] >= today.timestamp()
|
||||
|
||||
|
||||
def test_validate_will_settings():
|
||||
# Note: passing None triggers `will_settings = []` which then fails
|
||||
# on .get(). This is a latent bug — test passing a dict directly
|
||||
result = BalPlugin.validate_will_settings(None, {"baltx_fees": 0})
|
||||
assert result["baltx_fees"] == 100
|
||||
|
||||
# normal settings unchanged
|
||||
input_settings = {"baltx_fees": 50, "threshold": 1700000000, "locktime": 1800000000}
|
||||
result = BalPlugin.validate_will_settings(None, input_settings)
|
||||
assert result["baltx_fees"] == 50
|
||||
assert result["threshold"] == 1700000000
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_bt_create_and_str()
|
||||
test_bt_duration_to_days()
|
||||
test_bt_to_date_absolute()
|
||||
test_bt_to_date_relative()
|
||||
test_bt_to_date_years()
|
||||
test_bt_to_date_overflow()
|
||||
test_bt_to_timestamp()
|
||||
test_bt_repr()
|
||||
test_bt_edge_values()
|
||||
test_safe_fromtimestamp_normal()
|
||||
test_safe_fromtimestamp_nlocktime_max()
|
||||
test_safe_fromtimestamp_negative()
|
||||
test_balconfig_default()
|
||||
test_balconfig_set()
|
||||
test_default_will_settings_relative()
|
||||
test_default_will_settings()
|
||||
test_default_will_settings_absolute()
|
||||
test_validate_will_settings()
|
||||
print(f"[OK] All {sum(1 for k in dir() if k.startswith('test_'))} plugin_base tests passed")
|
||||
Reference in New Issue
Block a user