mirror of
https://github.com/RoboSats/robosats.git
synced 2025-07-18 08:43:14 +00:00
Add LND mock up classes
This commit is contained in:

committed by
Reckless_Satoshi

parent
09215be8a9
commit
4270f2d0a2
3
.github/workflows/django-test.yml
vendored
3
.github/workflows/django-test.yml
vendored
@ -22,7 +22,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
max-parallel: 4
|
max-parallel: 4
|
||||||
matrix:
|
matrix:
|
||||||
python-version: ["3.11.6"] # , "3.12"]
|
python-version: ["3.11.6", "3.12"]
|
||||||
|
|
||||||
services:
|
services:
|
||||||
db:
|
db:
|
||||||
@ -63,6 +63,7 @@ jobs:
|
|||||||
- name: 'Create .env File'
|
- name: 'Create .env File'
|
||||||
run: |
|
run: |
|
||||||
mv .env-sample .env
|
mv .env-sample .env
|
||||||
|
sed -i "s/USE_TOR='True'/USE_TOR='False'/" .env
|
||||||
|
|
||||||
- name: 'Wait for PostgreSQL to become ready'
|
- name: 'Wait for PostgreSQL to become ready'
|
||||||
run: |
|
run: |
|
||||||
|
@ -71,8 +71,6 @@ class LNDNode:
|
|||||||
5: "Insufficient local balance.",
|
5: "Insufficient local balance.",
|
||||||
}
|
}
|
||||||
|
|
||||||
is_testnet = lightningstub.GetInfo(lnrpc.GetInfoRequest()).testnet
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_version(cls):
|
def get_version(cls):
|
||||||
try:
|
try:
|
||||||
@ -93,8 +91,8 @@ class LNDNode:
|
|||||||
@classmethod
|
@classmethod
|
||||||
def estimate_fee(cls, amount_sats, target_conf=2, min_confs=1):
|
def estimate_fee(cls, amount_sats, target_conf=2, min_confs=1):
|
||||||
"""Returns estimated fee for onchain payouts"""
|
"""Returns estimated fee for onchain payouts"""
|
||||||
|
is_testnet = lightningstub.GetInfo(lnrpc.GetInfoRequest()).testnet
|
||||||
if cls.is_testnet:
|
if is_testnet:
|
||||||
dummy_address = "tb1qehyqhruxwl2p5pt52k6nxj4v8wwc3f3pg7377x"
|
dummy_address = "tb1qehyqhruxwl2p5pt52k6nxj4v8wwc3f3pg7377x"
|
||||||
else:
|
else:
|
||||||
dummy_address = "bc1qgxwaqe4m9mypd7ltww53yv3lyxhcfnhzzvy5j3"
|
dummy_address = "bc1qgxwaqe4m9mypd7ltww53yv3lyxhcfnhzzvy5j3"
|
||||||
|
0
tests/mocks/__init__.py
Normal file
0
tests/mocks/__init__.py
Normal file
32
tests/mocks/lnd.py
Normal file
32
tests/mocks/lnd.py
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
|
|
||||||
|
# Mock up of LND gRPC responses
|
||||||
|
class MockLightningStub:
|
||||||
|
def GetInfo(self, request):
|
||||||
|
response = MagicMock()
|
||||||
|
# Set the testnet attribute to True for testing purposes
|
||||||
|
response.testnet = True
|
||||||
|
return response
|
||||||
|
|
||||||
|
def EstimateFee(self, request):
|
||||||
|
response = MagicMock()
|
||||||
|
response.fee_sat = 1500
|
||||||
|
response.sat_per_vbyte = 13
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
class MockInvoicesStub:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class MockRouterStub:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class MockSignerStub:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class MockVersionerStub:
|
||||||
|
pass
|
@ -0,0 +1 @@
|
|||||||
|
oKrH73YD4ISQ0wzLNyPBeGp2OK7JTKghDfJe
|
@ -1,6 +1,7 @@
|
|||||||
import json
|
import json
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
from decouple import config
|
from decouple import config
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
@ -8,6 +9,13 @@ from django.test import Client, TestCase
|
|||||||
|
|
||||||
from api.models import Currency, Order
|
from api.models import Currency, Order
|
||||||
from api.tasks import cache_market
|
from api.tasks import cache_market
|
||||||
|
from tests.mocks.lnd import (
|
||||||
|
MockInvoicesStub,
|
||||||
|
MockLightningStub,
|
||||||
|
MockRouterStub,
|
||||||
|
MockSignerStub,
|
||||||
|
MockVersionerStub,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class TradeTest(TestCase):
|
class TradeTest(TestCase):
|
||||||
@ -22,6 +30,9 @@ class TradeTest(TestCase):
|
|||||||
User.objects.create_superuser(self.su_name, "super@user.com", self.su_pass)
|
User.objects.create_superuser(self.su_name, "super@user.com", self.su_pass)
|
||||||
|
|
||||||
def test_login_superuser(self):
|
def test_login_superuser(self):
|
||||||
|
"""
|
||||||
|
Test logging in as a superuser.
|
||||||
|
"""
|
||||||
path = "/coordinator/login/"
|
path = "/coordinator/login/"
|
||||||
data = {"username": self.su_name, "password": self.su_pass}
|
data = {"username": self.su_name, "password": self.su_pass}
|
||||||
response = self.client.post(path, data)
|
response = self.client.post(path, data)
|
||||||
@ -29,8 +40,8 @@ class TradeTest(TestCase):
|
|||||||
|
|
||||||
def get_robot_auth(self, robot_index):
|
def get_robot_auth(self, robot_index):
|
||||||
"""
|
"""
|
||||||
Crates an AUTH header that embeds token, pub_key and enc_priv_key into a single string
|
Create an AUTH header that embeds token, pub_key, and enc_priv_key into a single string
|
||||||
just as requested by the robosats token middleware.
|
as requested by the robosats token middleware.
|
||||||
"""
|
"""
|
||||||
with open(f"tests/robots/{robot_index}/b91_token", "r") as file:
|
with open(f"tests/robots/{robot_index}/b91_token", "r") as file:
|
||||||
b91_token = file.read()
|
b91_token = file.read()
|
||||||
@ -44,24 +55,14 @@ class TradeTest(TestCase):
|
|||||||
}
|
}
|
||||||
return headers, pub_key, enc_priv_key
|
return headers, pub_key, enc_priv_key
|
||||||
|
|
||||||
def create_robot(self, robot_index):
|
def assert_robot(self, response, pub_key, enc_priv_key, expected_nickname):
|
||||||
"""
|
|
||||||
Creates the robots in /tests/robots/{robot_index}
|
|
||||||
"""
|
|
||||||
path = "/api/robot/"
|
|
||||||
headers, pub_key, enc_priv_key = self.get_robot_auth(robot_index)
|
|
||||||
|
|
||||||
response = self.client.get(path, **headers)
|
|
||||||
data = json.loads(response.content.decode())
|
data = json.loads(response.content.decode())
|
||||||
|
|
||||||
with open(f"tests/robots/{robot_index}/nickname", "r") as file:
|
|
||||||
expected_nickname = file.read()
|
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
data["nickname"],
|
data["nickname"],
|
||||||
expected_nickname,
|
expected_nickname,
|
||||||
f"Robot {robot_index} created nickname is not MyopicRacket333",
|
"Robot created nickname is not MyopicRacket333",
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
data["public_key"], pub_key, "Returned public Kky does not match"
|
data["public_key"], pub_key, "Returned public Kky does not match"
|
||||||
@ -86,6 +87,20 @@ class TradeTest(TestCase):
|
|||||||
)
|
)
|
||||||
self.assertEqual(data["earned_rewards"], 0, "The new robot's rewards are not 0")
|
self.assertEqual(data["earned_rewards"], 0, "The new robot's rewards are not 0")
|
||||||
|
|
||||||
|
def create_robot(self, robot_index):
|
||||||
|
"""
|
||||||
|
Creates the robots in /tests/robots/{robot_index}
|
||||||
|
"""
|
||||||
|
path = "/api/robot/"
|
||||||
|
headers, pub_key, enc_priv_key = self.get_robot_auth(robot_index)
|
||||||
|
|
||||||
|
response = self.client.get(path, **headers)
|
||||||
|
|
||||||
|
with open(f"tests/robots/{robot_index}/nickname", "r") as file:
|
||||||
|
expected_nickname = file.read()
|
||||||
|
|
||||||
|
self.assert_robot(response, pub_key, enc_priv_key, expected_nickname)
|
||||||
|
|
||||||
def test_create_robots(self):
|
def test_create_robots(self):
|
||||||
"""
|
"""
|
||||||
Creates two robots to be used in the trade tests
|
Creates two robots to be used in the trade tests
|
||||||
@ -97,13 +112,16 @@ class TradeTest(TestCase):
|
|||||||
cache_market()
|
cache_market()
|
||||||
|
|
||||||
usd = Currency.objects.get(id=1)
|
usd = Currency.objects.get(id=1)
|
||||||
self.assertTrue(
|
self.assertIsInstance(
|
||||||
isinstance(usd.exchange_rate, Decimal), "Exchange rate is not decimal"
|
usd.exchange_rate,
|
||||||
|
Decimal,
|
||||||
|
f"Exchange rate is not a Decimal. Got {type(usd.exchange_rate)}",
|
||||||
)
|
)
|
||||||
self.assertLess(0, usd.exchange_rate, "Exchange rate is not higher than zero")
|
self.assertGreater(
|
||||||
self.assertTrue(
|
usd.exchange_rate, 0, "Exchange rate is not higher than zero"
|
||||||
isinstance(usd.timestamp, datetime),
|
)
|
||||||
"Externa price timestamp is not datetime",
|
self.assertIsInstance(
|
||||||
|
usd.timestamp, datetime, "External price timestamp is not a datetime"
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_create_order(
|
def test_create_order(
|
||||||
@ -147,18 +165,20 @@ class TradeTest(TestCase):
|
|||||||
data = json.loads(response.content.decode())
|
data = json.loads(response.content.decode())
|
||||||
|
|
||||||
# Checks
|
# Checks
|
||||||
self.assertTrue(isinstance(data["id"], int), "Order ID is not an integer")
|
self.assertIsInstance(data["id"], int, "Order ID is not an integer")
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
data["status"],
|
data["status"],
|
||||||
Order.Status.WFB,
|
Order.Status.WFB,
|
||||||
"Newly created order status is not 'Waiting for maker bond'",
|
"Newly created order status is not 'Waiting for maker bond'",
|
||||||
)
|
)
|
||||||
self.assertTrue(
|
self.assertIsInstance(
|
||||||
isinstance(datetime.fromisoformat(data["created_at"]), datetime),
|
datetime.fromisoformat(data["created_at"]),
|
||||||
|
datetime,
|
||||||
"Order creation timestamp is not datetime",
|
"Order creation timestamp is not datetime",
|
||||||
)
|
)
|
||||||
self.assertTrue(
|
self.assertIsInstance(
|
||||||
isinstance(datetime.fromisoformat(data["expires_at"]), datetime),
|
datetime.fromisoformat(data["expires_at"]),
|
||||||
|
datetime,
|
||||||
"Order expiry time is not datetime",
|
"Order expiry time is not datetime",
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
@ -166,13 +186,13 @@ class TradeTest(TestCase):
|
|||||||
)
|
)
|
||||||
self.assertEqual(data["currency"], 1, "Order for USD is not of currency USD")
|
self.assertEqual(data["currency"], 1, "Order for USD is not of currency USD")
|
||||||
self.assertIsNone(
|
self.assertIsNone(
|
||||||
data["amount"], "Order with range has a non null simple amount"
|
data["amount"], "Order with range has a non-null simple amount"
|
||||||
)
|
)
|
||||||
self.assertTrue(data["has_range"], "Order with range has a False has_range")
|
self.assertTrue(data["has_range"], "Order with range has a False has_range")
|
||||||
self.assertEqual(
|
self.assertAlmostEqual(
|
||||||
float(data["min_amount"]), min_amount, "Order min amount does not match"
|
float(data["min_amount"]), min_amount, "Order min amount does not match"
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertAlmostEqual(
|
||||||
float(data["max_amount"]), max_amount, "Order max amount does not match"
|
float(data["max_amount"]), max_amount, "Order max amount does not match"
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
@ -185,31 +205,41 @@ class TradeTest(TestCase):
|
|||||||
escrow_duration,
|
escrow_duration,
|
||||||
"Order escrow duration does not match",
|
"Order escrow duration does not match",
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertAlmostEqual(
|
||||||
float(data["bond_size"]), bond_size, "Order bond size does not match"
|
float(data["bond_size"]), bond_size, "Order bond size does not match"
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertAlmostEqual(
|
||||||
float(data["latitude"]), latitude, "Order latitude does not match"
|
float(data["latitude"]), latitude, "Order latitude does not match"
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertAlmostEqual(
|
||||||
float(data["longitude"]), longitude, "Order longitude does not match"
|
float(data["longitude"]), longitude, "Order longitude does not match"
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertAlmostEqual(
|
||||||
float(data["premium"]), premium, "Order premium does not match"
|
float(data["premium"]), premium, "Order premium does not match"
|
||||||
)
|
)
|
||||||
self.assertFalse(
|
self.assertFalse(
|
||||||
data["is_explicit"], "Relative pricing order has True is_explicit"
|
data["is_explicit"], "Relative pricing order has True is_explicit"
|
||||||
)
|
)
|
||||||
self.assertIsNone(
|
self.assertIsNone(
|
||||||
data["satoshis"], "Relative pricing order has non null Satoshis"
|
data["satoshis"], "Relative pricing order has non-null Satoshis"
|
||||||
)
|
)
|
||||||
self.assertIsNone(data["taker"], "New order's taker is not null")
|
self.assertIsNone(data["taker"], "New order's taker is not null")
|
||||||
|
|
||||||
with open(f"tests/robots/{robot_index}/nickname", "r") as file:
|
@patch("api.lightning.lightning_pb2_grpc.LightningStub", MockLightningStub)
|
||||||
maker_nickname = file.read()
|
@patch("api.lightning.invoices_pb2_grpc.InvoicesStub", MockInvoicesStub)
|
||||||
maker_id = User.objects.get(username=maker_nickname).id
|
@patch("api.lightning.router_pb2_grpc.RouterStub", MockRouterStub)
|
||||||
self.assertEqual(
|
@patch("api.lightning.signer_pb2_grpc.SignerStub", MockSignerStub)
|
||||||
data["maker"],
|
@patch("api.lightning.verrpc_pb2_grpc.VersionerStub", MockVersionerStub)
|
||||||
maker_id,
|
def test_maker_bond_locked(self):
|
||||||
"Maker user ID is not that of robot index {robot_index}",
|
self.test_create_order(
|
||||||
|
robot_index=1,
|
||||||
|
payment_method="Cash F2F",
|
||||||
|
min_amount=80,
|
||||||
|
max_amount=500,
|
||||||
|
premium=5,
|
||||||
|
public_duration=86000,
|
||||||
|
escrow_duration=8000,
|
||||||
|
bond_size=2,
|
||||||
|
latitude=0,
|
||||||
|
longitude=0,
|
||||||
)
|
)
|
||||||
|
Reference in New Issue
Block a user