Skip to content

Commit

Permalink
add initial tests for plugins repo CI
Browse files Browse the repository at this point in the history
  • Loading branch information
daywalker90 committed Feb 4, 2024
1 parent 2ba8b90 commit 9294015
Show file tree
Hide file tree
Showing 2 changed files with 375 additions and 0 deletions.
302 changes: 302 additions & 0 deletions tests/test_sling.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,302 @@
#!/usr/bin/python

from pyln.testing.fixtures import *
from pyln.testing.utils import sync_blockheight, wait_for, only_one
from pyln.client import RpcError
import os
import pytest
from util import get_plugin
from util import VERSION
import time


def test_basic(node_factory, get_plugin):
node = node_factory.get_node(
options={
'plugin': get_plugin
}
)
result = node.rpc.call("sling-version")
assert result is not None
assert isinstance(result, dict) is True
assert "version" in result
assert VERSION in result["version"]


def test_options(node_factory, get_plugin):
node = node_factory.get_node(
options={
'plugin': get_plugin,
'sling-refresh-peers-interval': 2,
'sling-refresh-aliasmap-interval': 3,
'sling-refresh-graph-interval': 599,
'sling-reset-liquidity-interval': 300,
'sling-depleteuptopercent': 0.33,
'sling-depleteuptoamount': 100000,
'sling-maxhops': 4,
'sling-candidates-min-age': 6,
'sling-paralleljobs': 4,
'sling-timeoutpay': 60,
'sling-max-htlc-count': 5,
'sling-stats-delete-failures-age': 0,
'sling-stats-delete-successes-age': 0,
'sling-stats-delete-failures-size': 0,
'sling-stats-delete-successes-size': 0,
}
)
configs = node.rpc.call("listconfigs")["configs"]
assert configs["sling-refresh-peers-interval"]["value_int"] == 2
assert configs["sling-refresh-aliasmap-interval"]["value_int"] == 3
assert configs["sling-refresh-graph-interval"]["value_int"] == 599
assert configs["sling-reset-liquidity-interval"]["value_int"] == 300
assert configs["sling-depleteuptopercent"]["value_str"] == "0.33"
assert configs["sling-depleteuptoamount"]["value_int"] == 100000
assert configs["sling-maxhops"]["value_int"] == 4
assert configs["sling-candidates-min-age"]["value_int"] == 6
assert configs["sling-paralleljobs"]["value_int"] == 4
assert configs["sling-timeoutpay"]["value_int"] == 60
assert configs["sling-max-htlc-count"]["value_int"] == 5
assert configs["sling-stats-delete-failures-age"]["value_int"] == 0
assert configs["sling-stats-delete-successes-age"]["value_int"] == 0
assert configs["sling-stats-delete-failures-size"]["value_int"] == 0
assert configs["sling-stats-delete-successes-size"]["value_int"] == 0


def test_option_errors(node_factory, get_plugin):
node = node_factory.get_node(
options={
'plugin': get_plugin,
'sling-refresh-peers-interval': 0,
}
)
assert node.daemon.is_in_log(
(r'sling-refresh-peers-interval must be '
r'greater than or equal to 1'))

node = node_factory.get_node(
options={
'plugin': get_plugin,
'sling-refresh-aliasmap-interval': 0,
}
)
assert node.daemon.is_in_log(
(r'sling-refresh-aliasmap-interval must be '
r'greater than or equal to 1'))

node = node_factory.get_node(
options={
'plugin': get_plugin,
'sling-refresh-graph-interval': 0,
}
)
assert node.daemon.is_in_log(
(r'sling-refresh-graph-interval must be '
r'greater than or equal to 1'))

node = node_factory.get_node(
options={
'plugin': get_plugin,
'sling-reset-liquidity-interval': 0,
}
)
assert node.daemon.is_in_log(
(r'sling-reset-liquidity-interval must be '
r'greater than or equal to 1'))

node = node_factory.get_node(
options={
'plugin': get_plugin,
'sling-depleteuptopercent': 1,
}
)
assert node.daemon.is_in_log(
r'sling-depleteuptopercent needs to be greater than 0 and <1')

node = node_factory.get_node(
options={
'plugin': get_plugin,
'sling-depleteuptoamount': -10,
}
)
assert node.daemon.is_in_log(
(r'sling-depleteuptoamount needs to be a positive number'))

node = node_factory.get_node(
options={
'plugin': get_plugin,
'sling-maxhops': 0,
}
)
assert node.daemon.is_in_log(
(r'sling-maxhops must be '
r'greater than or equal to 2'))

node = node_factory.get_node(
options={
'plugin': get_plugin,
'sling-candidates-min-age': -10,
}
)
assert node.daemon.is_in_log(
r'sling-candidates-min-age needs to be a positive number')

node = node_factory.get_node(
options={
'plugin': get_plugin,
'sling-paralleljobs': 0,
}
)
assert node.daemon.is_in_log(
(r'sling-paralleljobs must be '
r'greater than or equal to 1'))

node = node_factory.get_node(
options={
'plugin': get_plugin,
'sling-timeoutpay': 0,
}
)
assert node.daemon.is_in_log(
r'sling-timeoutpay must be greater than or equal to 1')

node = node_factory.get_node(
options={
'plugin': get_plugin,
'sling-max-htlc-count': 0,
}
)
assert node.daemon.is_in_log(
r'sling-max-htlc-count must be greater than or equal to 1')

node = node_factory.get_node(
options={
'plugin': get_plugin,
'sling-stats-delete-failures-age': 99999999,
}
)
assert node.daemon.is_in_log(
(r'sling-stats-delete-failures-age needs to be '
r'a positive number and smaller than'))

node = node_factory.get_node(
options={
'plugin': get_plugin,
'sling-stats-delete-successes-age': 99999999,
}
)
assert node.daemon.is_in_log(
(r'sling-stats-delete-successes-age needs to be '
r'a positive number and smaller than'))

node = node_factory.get_node(
options={
'plugin': get_plugin,
'sling-stats-delete-failures-size': -10,
}
)
assert node.daemon.is_in_log(
r'sling-stats-delete-failures-size needs to be a positive number')

node = node_factory.get_node(
options={
'plugin': get_plugin,
'sling-stats-delete-successes-size': -10,
}
)
assert node.daemon.is_in_log(
r'sling-stats-delete-successes-size needs to be a positive number')


def test_maxhops_2(node_factory, bitcoind, get_plugin):
l1, l2 = node_factory.get_nodes(2,
opts={
'plugin': get_plugin,
'sling-maxhops': 2,
'sling-refresh-graph-interval': 1
}
)
l1.fundwallet(10_000_000)
l2.fundwallet(10_000_000)
l1.rpc.connect(l2.info['id'], 'localhost', l2.port)
l1.rpc.fundchannel(l2.info['id'], 1_000_000, mindepth=1)
l2.rpc.fundchannel(l1.info['id'], 1_000_000, mindepth=1)

bitcoind.generate_block(1)
sync_blockheight(bitcoind, [l1, l2])

chans = l2.rpc.listpeerchannels(l1.info['id'])["channels"]
for chan in chans:
if chan["opener"] == "local":
cl2 = chan["short_channel_id"]
else:
cl1 = chan["short_channel_id"]
l2.wait_channel_active(cl1)
l2.wait_channel_active(cl2)

# wait for plugin gossip refresh
time.sleep(2)

l1.rpc.call(
"sling-job", {"scid": cl2, "direction": "pull",
"amount": 100_000, "maxppm": 1000, "outppm": 1000})
l1.rpc.call("sling-go", [])
l1.daemon.wait_for_log(r"already balanced. Taking a break")
wait_for(lambda: l1.rpc.listpeerchannels(
l2.info['id'])['channels'][0]['to_us_msat'] >= 400_000_000)
wait_for(lambda: l1.rpc.listpeerchannels(
l2.info['id'])['channels'][0]['to_us_msat'] <= 600_000_000)


def test_pull_and_push(node_factory, bitcoind, get_plugin):
l1, l2, l3 = node_factory.get_nodes(3,
opts={
'plugin': get_plugin,
'sling-refresh-graph-interval': 1
}
)
l1.fundwallet(10_000_000)
l2.fundwallet(10_000_000)
l3.fundwallet(10_000_000)
l1.rpc.connect(l2.info['id'], 'localhost', l2.port)
l2.rpc.connect(l3.info['id'], 'localhost', l3.port)
l3.rpc.connect(l1.info['id'], 'localhost', l1.port)
l1.rpc.fundchannel(l2.info['id'], 1_000_000, mindepth=1, announce=True)
l2.rpc.fundchannel(l3.info['id'], 1_000_000, mindepth=1, announce=True)
l3.rpc.fundchannel(l1.info['id'], 1_000_000, mindepth=1, announce=True)

bitcoind.generate_block(6)
sync_blockheight(bitcoind, [l1, l2, l3])

cl1 = l1.rpc.listpeerchannels(l2.info['id'])[
"channels"][0]["short_channel_id"]
cl2 = l2.rpc.listpeerchannels(l3.info['id'])[
"channels"][0]["short_channel_id"]
cl3 = l3.rpc.listpeerchannels(l1.info['id'])[
"channels"][0]["short_channel_id"]

for n in [l1, l2, l3]:
for scid in [cl1, cl2, cl3]:
n.wait_channel_active(scid)

# wait for plugin gossip refresh
time.sleep(2)

l1.rpc.call(
"sling-job", {"scid": cl3, "direction": "pull",
"amount": 100_000, "maxppm": 1000, "outppm": 1000})
l1.rpc.call("sling-go", [])
l1.daemon.wait_for_log(r"already balanced. Taking a break")
wait_for(lambda: only_one(l1.rpc.listpeerchannels(
l3.info['id'])['channels'])['to_us_msat'] >= 400_000_000)
wait_for(lambda: only_one(l1.rpc.listpeerchannels(
l3.info['id'])['channels'])['to_us_msat'] <= 600_000_000)

l1.rpc.call("sling-deletejob", ["all"])
l1.rpc.call(
"sling-job", {"scid": cl3, "direction": "push", "target": 1,
"amount": 100_000, "maxppm": 1000, "outppm": 0,
"depleteuptopercent": 0})
l1.rpc.call("sling-go", [])
l1.daemon.wait_for_log(r"already balanced. Taking a break")
wait_for(lambda: only_one(l1.rpc.listpeerchannels(
l3.info['id'])['channels'])['to_us_msat'] <= 120_000_000)
73 changes: 73 additions & 0 deletions tests/util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import string
import random
import logging
import os
import pytest
from pathlib import Path
import requests
import tarfile
import platform

VERSION = "v1.4.1"
RUST_PROFILE = os.environ.get("RUST_PROFILE", "debug")
COMPILED_PATH = Path.cwd() / "target" / RUST_PROFILE / \
"sling"


@pytest.fixture
def get_plugin(directory):
downloaded_plugin_path = Path(
os.path.join(directory, "sling.tar.gz"))
extracted_plugin_path = Path(os.path.join(directory, "sling"))
if COMPILED_PATH.is_file():
return COMPILED_PATH
elif extracted_plugin_path.is_file():
return extracted_plugin_path
else:
architecture = get_architecture()

url = (f"https://github.com/daywalker90/sling/releases/download/"
f"{VERSION}/sling-{VERSION}-{architecture}.tar.gz")
response = requests.get(url)
with open(downloaded_plugin_path, "wb") as file:
file.write(response.content)

with tarfile.open(downloaded_plugin_path, "r:gz") as tar:
tar.extractall(directory)

return extracted_plugin_path


def get_architecture():
machine = platform.machine()

if machine == 'x86_64':
return 'x86_64-linux-gnu'
elif machine == 'armv7l':
return 'armv7-linux-gnueabihf'
elif machine == 'aarch64':
return 'aarch64-linux-gnu'
else:
raise RuntimeError(
f"No self-compiled binary found and "
f"unsupported release-architecture: {machine}")


def generate_random_label():
label_length = 8
random_label = ''.join(random.choice(string.ascii_letters)
for _ in range(label_length))
return random_label


def generate_random_number():
return random.randint(1, 20_000_000_000_000_00_000)


def pay_with_thread(rpc, bolt11):
LOGGER = logging.getLogger(__name__)
try:
rpc.dev_pay(bolt11, dev_use_shadow=False)
except Exception as e:
LOGGER.debug(f"holdinvoice: Error paying payment hash:{e}")
pass

0 comments on commit 9294015

Please sign in to comment.