From b2a130afca86bee90ed37256393756f66a217e7c Mon Sep 17 00:00:00 2001 From: Reckless_Satoshi Date: Fri, 17 Nov 2023 15:16:03 +0000 Subject: [PATCH] Add LN payout test --- api/lightning/lnd.py | 2 +- docker-tests.yml | 26 +++++++++++++++++++ tests/test_api.py | 7 ++++++ tests/test_trade_pipeline.py | 48 +++++++++++++++++++++++++++++++----- 4 files changed, 76 insertions(+), 7 deletions(-) diff --git a/api/lightning/lnd.py b/api/lightning/lnd.py index 2d2438db..24ae5bd0 100644 --- a/api/lightning/lnd.py +++ b/api/lightning/lnd.py @@ -87,7 +87,7 @@ class LNDNode: log("verstub.GetVersion", request, response) return "v" + response.version except Exception as e: - print(f"Cannot get CLN version: {e}") + print(f"Cannot get LND version: {e}") return "Not installed" @classmethod diff --git a/docker-tests.yml b/docker-tests.yml index 387a9133..782609ea 100644 --- a/docker-tests.yml +++ b/docker-tests.yml @@ -175,6 +175,32 @@ services: POSTGRES_DB: 'postgres' network_mode: service:bitcoind + # celery-worker: + # image: robosats-image + # pull_policy: never + # container_name: celery-worker + # restart: always + # environment: + # DEVELOPMENT: True + # TESTING: True + # USE_TOR: False + # MACAROON_PATH: 'data/chain/bitcoin/regtest/admin.macaroon' + # CLN_DIR: '/cln/regtest/' + # BITCOIND_RPCURL: 'http://127.0.0.1:18443' + # BITCOIND_RPCUSER: 'test' + # BITCOIND_RPCPASSWORD: 'test' + # SKIP_COLLECT_STATIC: "true" + # env_file: + # - ${ROBOSATS_ENVS_FILE} + # volumes: + # - .:/usr/src/robosats + # - lnd:/lnd + # - cln:/cln + # command: celery -A robosats worker --loglevel=INFO --concurrency 2 --max-tasks-per-child=4 --max-memory-per-child=200000 + # depends_on: + # - redis + # network_mode: service:bitcoind + volumes: redisdata: bitcoin: diff --git a/tests/test_api.py b/tests/test_api.py index 7e30e835..0d9f8abf 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -26,3 +26,10 @@ class BaseAPITestCase(APITestCase): if response.request["PATH_INFO"] not in skip_paths: schema_tester.validate_response(response=response, **kwargs) + + def assertIsHash(self, s): + hex_chars = set("0123456789abcdefABCDEF") + self.assertTrue( + len(s) == 64 and all(c in hex_chars for c in s), + "The hash is not 64 hex characters", + ) diff --git a/tests/test_trade_pipeline.py b/tests/test_trade_pipeline.py index bc3e8f85..d999b1e4 100644 --- a/tests/test_trade_pipeline.py +++ b/tests/test_trade_pipeline.py @@ -2,6 +2,7 @@ import json import random from datetime import datetime from decimal import Decimal +from unittest.mock import patch from decouple import config from django.contrib.auth.models import User @@ -9,7 +10,7 @@ from django.urls import reverse from api.management.commands.follow_invoices import Command as FollowInvoices from api.models import Currency, Order -from api.tasks import cache_market +from api.tasks import cache_market, follow_send_payment from control.models import BalanceLog from control.tasks import compute_node_balance from tests.node_utils import ( @@ -356,12 +357,16 @@ class TradeTest(BaseAPITestCase): # Cancel order to avoid leaving pending HTLCs after a successful test self.cancel_order(data["id"]) - def check_for_locked_bonds(self): + def follow_hold_invoices(self): # A background thread checks every 5 second the status of invoices. We invoke directly during test. - # It will ask LND via gRPC. In our test, the request/response from LND is mocked, and it will return fake invoice status "ACCEPTED" follow_invoices = FollowInvoices() follow_invoices.follow_hold_invoices() + def send_payments(self): + # A background thread checks every 5 second whether there are outgoing payments. We invoke directly during test. + follow_invoices = FollowInvoices() + follow_invoices.send_payments() + def make_and_publish_order(self, maker_form, robot_index=1): # Make an order order_made_response = self.make_order(maker_form, robot_index) @@ -375,7 +380,7 @@ class TradeTest(BaseAPITestCase): pay_invoice("robot", invoice) # Check for invoice locked (the mocked LND will return ACCEPTED) - self.check_for_locked_bonds() + self.follow_hold_invoices() # Get order response = self.get_order(order_made_data["id"]) @@ -509,7 +514,7 @@ class TradeTest(BaseAPITestCase): pay_invoice("robot", invoice) # Check for invoice locked (the mocked LND will return ACCEPTED) - self.check_for_locked_bonds() + self.follow_hold_invoices() # Get order response = self.get_order(order_taken_data["id"], taker_index) @@ -578,7 +583,7 @@ class TradeTest(BaseAPITestCase): pay_invoice("robot", invoice) # Check for invoice locked (the mocked LND will return ACCEPTED) - self.check_for_locked_bonds() + self.follow_hold_invoices() # Get order response = self.get_order(locked_taker_response_data["id"], taker_index) @@ -814,3 +819,34 @@ class TradeTest(BaseAPITestCase): self.assertFalse(data["maker_locked"]) self.assertFalse(data["taker_locked"]) self.assertFalse(data["escrow_locked"]) + + @patch("api.tasks.follow_send_payment.delay", follow_send_payment) + def test_successful_LN(self): + """ + Tests a trade from order creation until Sats sent to buyer + """ + maker_index = 1 + taker_index = 2 + maker_form = self.maker_form_buy_with_range + take_amount = round( + random.uniform(maker_form["min_amount"], maker_form["max_amount"]), 2 + ) + + response = self.trade_to_confirm_fiat_received_LN( + maker_form, take_amount, maker_index, taker_index + ) + + # Invoke the background thread that will call the celery-worker to follow_send_payment() + self.send_payments() + + response = self.get_order(response.json()["id"], maker_index) + data = response.json() + + self.assertEqual(response.status_code, 200) + self.assertResponse(response) + + self.assertEqual(data["status_message"], Order.Status(Order.Status.SUC).label) + self.assertTrue(data["is_fiat_sent"]) + self.assertFalse(data["is_disputed"]) + self.assertIsHash(data["maker_summary"]["preimage"]) + self.assertIsHash(data["maker_summary"]["payment_hash"])