Add order expiry tests

This commit is contained in:
Reckless_Satoshi 2023-11-22 13:16:25 +00:00
parent 14340fd64b
commit 62ef86f1b4
No known key found for this signature in database
GPG Key ID: 9C4585B561315571
5 changed files with 180 additions and 53 deletions

View File

@ -8,68 +8,65 @@ from api.models import Order
class Command(BaseCommand): class Command(BaseCommand):
help = "Follows all active hold invoices" help = "Follows all active orders and make them expire if needed."
# def add_arguments(self, parser): do_nothing = [
# parser.add_argument('debug', nargs='+', type=boolean) Order.Status.UCA,
Order.Status.EXP,
Order.Status.DIS,
Order.Status.CCA,
Order.Status.PAY,
Order.Status.SUC,
Order.Status.FAI,
Order.Status.MLD,
Order.Status.TLD,
Order.Status.WFR,
]
def clean_orders(self, *args, **options): def clean_orders(self):
"""Continuously checks order expiration times for 1 hour. If order """Continuously checks order expiration times. If order
has expires, it calls the logics module for expiration handling.""" has expires, it calls the logics module for expiration handling."""
do_nothing = [ queryset = Order.objects.exclude(status__in=self.do_nothing)
Order.Status.UCA, queryset = queryset.filter(
Order.Status.EXP, expires_at__lt=timezone.now()
Order.Status.DIS, ) # expires at lower than now
Order.Status.CCA,
Order.Status.PAY,
Order.Status.SUC,
Order.Status.FAI,
Order.Status.MLD,
Order.Status.TLD,
Order.Status.WFR,
]
while True: debug = {}
time.sleep(5) debug["num_expired_orders"] = len(queryset)
debug["expired_orders"] = []
debug["failed_order_expiry"] = []
debug["reason_failure"] = []
queryset = Order.objects.exclude(status__in=do_nothing) for idx, order in enumerate(queryset):
queryset = queryset.filter( context = str(order) + " was " + Order.Status(order.status).label
expires_at__lt=timezone.now() try:
) # expires at lower than now if Logics.order_expires(order): # Order send to expire here
debug["expired_orders"].append({idx: context})
debug = {} # It should not happen, but if it cannot locate the hold invoice
debug["num_expired_orders"] = len(queryset) # it probably was cancelled by another thread, make it expire anyway.
debug["expired_orders"] = [] except Exception as e:
debug["failed_order_expiry"] = [] debug["failed_order_expiry"].append({idx: context})
debug["reason_failure"] = [] debug["reason_failure"].append({idx: str(e)})
for idx, order in enumerate(queryset): if "unable to locate invoice" in str(e):
context = str(order) + " was " + Order.Status(order.status).label self.stdout.write(str(e))
try: order.update_status(Order.Status.EXP)
if Logics.order_expires(order): # Order send to expire here debug["expired_orders"].append({idx: context})
debug["expired_orders"].append({idx: context})
# It should not happen, but if it cannot locate the hold invoice if debug["num_expired_orders"] > 0:
# it probably was cancelled by another thread, make it expire anyway. self.stdout.write(str(timezone.now()))
except Exception as e: self.stdout.write(str(debug))
debug["failed_order_expiry"].append({idx: context})
debug["reason_failure"].append({idx: str(e)})
if "unable to locate invoice" in str(e):
self.stdout.write(str(e))
order.update_status(Order.Status.EXP)
debug["expired_orders"].append({idx: context})
if debug["num_expired_orders"] > 0:
self.stdout.write(str(timezone.now()))
self.stdout.write(str(debug))
def handle(self, *args, **options): def handle(self, *args, **options):
"""Never mind database locked error, keep going, print them out. """Never mind database locked error, keep going, print them out.
Not an issue with PostgresQL""" Not an issue with PostgresQL"""
try: try:
self.clean_orders() while True:
self.clean_orders()
time.sleep(5)
except Exception as e: except Exception as e:
if "database is locked" in str(e): if "database is locked" in str(e):
self.stdout.write("database is locked") self.stdout.write("database is locked")

View File

@ -7,19 +7,20 @@ from decouple import config
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.urls import reverse from django.urls import reverse
from api.management.commands.clean_orders import Command as CleanOrders
from api.management.commands.follow_invoices import Command as FollowInvoices from api.management.commands.follow_invoices import Command as FollowInvoices
from api.models import Currency, Order from api.models import Currency, Order
from api.tasks import cache_market, follow_send_payment from api.tasks import cache_market, follow_send_payment
from control.models import BalanceLog from control.models import BalanceLog
from control.tasks import compute_node_balance, do_accounting from control.tasks import compute_node_balance, do_accounting
from tests.node_utils import ( from tests.test_api import BaseAPITestCase
from tests.utils.node import (
add_invoice, add_invoice,
create_address, create_address,
pay_invoice, pay_invoice,
set_up_regtest_network, set_up_regtest_network,
) )
from tests.pgp_utils import sign_message from tests.utils.pgp import sign_message
from tests.test_api import BaseAPITestCase
def read_file(file_path): def read_file(file_path):
@ -358,8 +359,13 @@ class TradeTest(BaseAPITestCase):
def follow_hold_invoices(self): def follow_hold_invoices(self):
# A background thread checks every 5 second the status of invoices. We invoke directly during test. # A background thread checks every 5 second the status of invoices. We invoke directly during test.
follow_invoices = FollowInvoices() follower = FollowInvoices()
follow_invoices.follow_hold_invoices() follower.follow_hold_invoices()
def clean_orders(self):
# A background thread checks every 5 second order expirations. We invoke directly during test.
cleaner = CleanOrders()
cleaner.clean_orders()
def send_payments(self): def send_payments(self):
# A background thread checks every 5 second whether there are outgoing payments. We invoke directly during test. # A background thread checks every 5 second whether there are outgoing payments. We invoke directly during test.
@ -906,6 +912,130 @@ class TradeTest(BaseAPITestCase):
data["bad_request"], "This order has been cancelled collaborativelly" data["bad_request"], "This order has been cancelled collaborativelly"
) )
def test_created_order_expires(self):
"""
Tests the expiration of a public order
"""
maker_form = self.maker_form_buy_with_range
response = self.make_order(maker_form)
# Change order expiry to now
order = Order.objects.get(id=response.json()["id"])
order.expires_at = datetime.now()
order.save()
# Make orders expire
self.clean_orders()
response = self.get_order(response.json()["id"])
data = response.json()
self.assertEqual(response.status_code, 200)
self.assertResponse(response)
self.assertEqual(
data["status"],
Order.Status.EXP,
)
self.assertEqual(
data["expiry_message"],
Order.ExpiryReasons(Order.ExpiryReasons.NMBOND).label,
)
self.assertEqual(data["expiry_reason"], Order.ExpiryReasons.NMBOND)
def test_public_order_expires(self):
"""
Tests the expiration of a public order
"""
maker_form = self.maker_form_buy_with_range
response = self.make_and_publish_order(maker_form)
# Change order expiry to now
order = Order.objects.get(id=response.json()["id"])
order.expires_at = datetime.now()
order.save()
# Make orders expire
self.clean_orders()
response = self.get_order(response.json()["id"])
data = response.json()
self.assertEqual(response.status_code, 200)
self.assertResponse(response)
self.assertEqual(
data["status"],
Order.Status.EXP,
)
self.assertEqual(
data["expiry_message"],
Order.ExpiryReasons(Order.ExpiryReasons.NTAKEN).label,
)
self.assertEqual(data["expiry_reason"], Order.ExpiryReasons.NTAKEN)
def test_taken_order_expires(self):
"""
Tests the expiration of a public order
"""
maker_form = self.maker_form_buy_with_range
response = self.make_and_lock_contract(maker_form)
# Change order expiry to now
order = Order.objects.get(id=response.json()["id"])
order.expires_at = datetime.now()
order.save()
# Make orders expire
self.clean_orders()
response = self.get_order(response.json()["id"])
data = response.json()
self.assertEqual(response.status_code, 200)
self.assertResponse(response)
self.assertEqual(
data["status"],
Order.Status.EXP,
)
self.assertEqual(
data["expiry_message"],
Order.ExpiryReasons(Order.ExpiryReasons.NESINV).label,
)
self.assertEqual(data["expiry_reason"], Order.ExpiryReasons.NESINV)
def test_escrow_locked_expires(self):
"""
Tests the expiration of a public order
"""
maker_form = self.maker_form_buy_with_range
response = self.trade_to_locked_escrow(maker_form)
# Change order expiry to now
order = Order.objects.get(id=response.json()["id"])
order.expires_at = datetime.now()
order.save()
# Make orders expire
self.clean_orders()
response = self.get_order(response.json()["id"])
data = response.json()
self.assertEqual(response.status_code, 200)
self.assertResponse(response)
self.assertEqual(
data["status"],
Order.Status.EXP,
)
self.assertEqual(
data["expiry_message"],
Order.ExpiryReasons(Order.ExpiryReasons.NINVOI).label,
)
self.assertEqual(data["expiry_reason"], Order.ExpiryReasons.NINVOI)
def test_ticks(self): def test_ticks(self):
""" """
Tests the historical ticks serving endpoint after creating a contract Tests the historical ticks serving endpoint after creating a contract

0
tests/utils/__init__.py Normal file
View File