Skip to content

Commit

Permalink
test: add smoke tests with pyln-testing
Browse files Browse the repository at this point in the history
  • Loading branch information
michael1011 committed Sep 7, 2024
1 parent ea3f498 commit 81d3e2a
Show file tree
Hide file tree
Showing 19 changed files with 422 additions and 215 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ jobs:

- name: Python formatting
run: poetry run ruff format --check
working-directory: ./tests
working-directory: ./tests-regtest

- name: Regtest startup
run: make regtest-start
Expand Down
5 changes: 3 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ target/
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
.idea/

tests/hold/protos/*
!tests/hold/protos/__init__.py
tests/*.tar.gz
tests-regtest/hold/protos/*
!tests-regtest/hold/protos/__init__.py
**/__pycache__
build/
12 changes: 6 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,19 @@ build-release:
cargo build --release

python-install:
cd tests && poetry install
cd tests-regtest && poetry install

python-lint:
cd tests && poetry run ruff check
cd tests-regtest && poetry run ruff check . ../tests

python-lint-fix:
cd tests && poetry run ruff check --fix
cd tests-regtest && poetry run ruff check --fix . ../tests

python-format:
cd tests && poetry run ruff format
cd tests-regtest && poetry run ruff format . ../tests

python-protos:
cd tests && poetry run python -m grpc_tools.protoc -I ../protos \
cd tests-regtest && poetry run python -m grpc_tools.protoc -I ../protos \
--python_out=hold/protos \
--pyi_out=hold/protos \
--grpc_python_out=hold/protos \
Expand Down Expand Up @@ -50,7 +50,7 @@ db-stop:
docker stop hold-db

integration-tests:
cd tests && poetry run pytest
cd tests-regtest && poetry run pytest hold/

binaries:
docker buildx build . -o=build --target=binaries
Expand Down
6 changes: 5 additions & 1 deletion src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,8 @@ pub const OPTION_GRPC_HOST: options::DefaultStringConfigOption =
options::ConfigOption::new_str_with_default("hold-grpc-host", "127.0.0.1", "hold gRPC host");

pub const OPTION_GRPC_PORT: options::DefaultIntegerConfigOption =
options::ConfigOption::new_i64_with_default("hold-grpc-port", 9292, "hold gRPC post");
options::ConfigOption::new_i64_with_default(
"hold-grpc-port",
9292,
"hold gRPC post; set to -1 to disable",
);
10 changes: 10 additions & 0 deletions src/grpc/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,16 @@ where
}

pub async fn start(&self) -> Result<()> {
if self.port == -1 {
info!("Not starting gRPC server");
let token = self.cancellation_token.clone();
tokio::spawn(async move {
token.cancelled().await;
})
.await?;
return Ok(());
}

// Always listen to all interfaces on regtest
let socket_addr = SocketAddr::new(
IpAddr::from_str(if !self.is_regtest {
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
404 changes: 204 additions & 200 deletions tests/poetry.lock → tests-regtest/poetry.lock

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions tests/pyproject.toml → tests-regtest/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ license = "MIT"

[tool.poetry.dependencies]
python = "^3.10"
pytest = "^8.3.2"
ruff = "^0.6.1"
grpcio = "^1.65.5"
grpcio-tools = "^1.65.5"
ruff = "^0.6.4"
grpcio = "^1.66.1"
grpcio-tools = "^1.66.1"
bolt11 = "^2.1.0"
pytest = "^8.3.2"

[build-system]
requires = ["poetry-core"]
Expand All @@ -24,5 +24,5 @@ exclude = ["hold/protos"]
select = ["ALL"]
ignore = [
"D100", "D101", "D102", "D103", "D104", "D107", "D211", "D212", "S605", "D203", "ISC001", "COM812", "S101",
"PLR2004", "PT011", "TCH001"
"PLR2004", "PT011", "TCH001", "F403", "F405"
]
3 changes: 3 additions & 0 deletions tests-regtest/pytest.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[pytest]
python_files = "regtest_*.py"

Empty file added tests/__init__.py
Empty file.
17 changes: 17 additions & 0 deletions tests/setup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/bin/bash
set -x

script_dir=$(dirname -- "$(readlink -f -- "$0")")
cargo_toml_path="$script_dir/../Cargo.toml"
version=$(awk -F'=' '/^\[package\]/ { in_package = 1 } in_package && /version/ { gsub(/[" ]/, "", $2); print $2; exit }' "$cargo_toml_path")

artifact_url="https://github.com/BoltzExchange/hold/releases/download/v$version/hold-linux-amd64.tar.gz"
archive_file="$script_dir/hold.tar.gz"

if ! curl -L "$artifact_url" -o "$archive_file"; then
exit 1
fi

if ! tar -xzvf "$archive_file" -C "$script_dir"; then
exit 1
fi
168 changes: 168 additions & 0 deletions tests/test_hold.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
from __future__ import annotations

from hashlib import sha256
from pathlib import Path
from threading import Thread

from pyln.client import RpcError
from pyln.testing.fixtures import *


def new_preimage() -> tuple[str, str]:
preimage = os.urandom(32)
return preimage.hex(), sha256(preimage).hexdigest()


@pytest.fixture
def plugin_path() -> Path:
return Path.cwd() / "tests" / "build" / "hold-linux-amd64"


def pay_with_thread(rpc: any, bolt11: str) -> None:
logger = logging.getLogger(__name__)
try:
rpc.dev_pay(bolt11, dev_use_shadow=False)
except RpcError as e:
logger.info("error paying invoice with payment hash: %s", e)


def test_holdinvoice(node_factory: NodeFactory, plugin_path: Path) -> None:
node = node_factory.get_node(
options={"important-plugin": plugin_path, "hold-grpc-port": "-1"}
)

amount = 1_000
_, payment_hash = new_preimage()
res = node.rpc.call(
"holdinvoice",
{
"amount": amount,
"payment_hash": payment_hash,
},
)

decoded = node.rpc.call("decode", [res["bolt11"]])

assert decoded["payment_hash"] == payment_hash
assert decoded["amount_msat"] == amount


def test_listholdinvoices(node_factory: NodeFactory, plugin_path: Path) -> None:
node = node_factory.get_node(
options={"important-plugin": plugin_path, "hold-grpc-port": "-1"}
)

hashes = [new_preimage()[0] for _ in range(5)]
invoices = [
node.rpc.call(
"holdinvoice",
{
"amount": 1_000,
"payment_hash": payment_hash,
},
)["bolt11"]
for payment_hash in hashes
]

assert len(node.rpc.call("listholdinvoices")["holdinvoices"]) == len(hashes)
assert node.rpc.call(
"listholdinvoices", {"payment_hash": hashes[0]}
) == node.rpc.call("listholdinvoices", {"bolt11": invoices[0]})


def test_settle(
node_factory: NodeFactory, bitcoind: BitcoinD, plugin_path: Path
) -> None:
l1 = node_factory.get_node(
options={"important-plugin": plugin_path, "hold-grpc-port": "-1"}
)
l2 = node_factory.get_node()

l1.rpc.connect(l2.info["id"], "localhost", l2.port)
cl1, _ = l1.fundchannel(l2, 1_000_000)
cl2, _ = l2.fundchannel(l1, 1_000_000)

bitcoind.generate_block(6)

l1.wait_channel_active(cl1)
l1.wait_channel_active(cl2)

preimage, payment_hash = new_preimage()
amount = 1_000
invoice = l1.rpc.call(
"holdinvoice", {"amount": amount, "payment_hash": payment_hash}
)["bolt11"]

Thread(target=pay_with_thread, args=(l2, invoice)).start()
time.sleep(2)

assert (
l1.rpc.call(
"listholdinvoices",
{
"payment_hash": payment_hash,
},
)["holdinvoices"][0]["state"]
== "accepted"
)

l1.rpc.call("settleholdinvoice", {"preimage": preimage})

assert (
l1.rpc.call(
"listholdinvoices",
{
"payment_hash": payment_hash,
},
)["holdinvoices"][0]["state"]
== "paid"
)


def test_cancel(
node_factory: NodeFactory, bitcoind: BitcoinD, plugin_path: Path
) -> None:
l1 = node_factory.get_node(
options={"important-plugin": plugin_path, "hold-grpc-port": "-1"}
)
l2 = node_factory.get_node()

l1.rpc.connect(l2.info["id"], "localhost", l2.port)
cl1, _ = l1.fundchannel(l2, 1_000_000)
cl2, _ = l2.fundchannel(l1, 1_000_000)

bitcoind.generate_block(6)

l1.wait_channel_active(cl1)
l1.wait_channel_active(cl2)

_, payment_hash = new_preimage()
amount = 1_000
invoice = l1.rpc.call(
"holdinvoice", {"amount": amount, "payment_hash": payment_hash}
)["bolt11"]

Thread(target=pay_with_thread, args=(l2, invoice)).start()
time.sleep(2)

assert (
l1.rpc.call(
"listholdinvoices",
{
"payment_hash": payment_hash,
},
)["holdinvoices"][0]["state"]
== "accepted"
)

l1.rpc.call("cancelholdinvoice", {"payment_hash": payment_hash})

assert (
l1.rpc.call(
"listholdinvoices",
{
"payment_hash": payment_hash,
},
)["holdinvoices"][0]["state"]
== "cancelled"
)

0 comments on commit 81d3e2a

Please sign in to comment.