diff --git a/api/admin.py b/api/admin.py
index d853d7da..751a2a07 100644
--- a/api/admin.py
+++ b/api/admin.py
@@ -133,7 +133,13 @@ class OrderAdmin(AdminChangeLinksMixin, admin.ModelAdmin):
if not obj.logs:
return format_html("No logs were recorded")
with_hyperlinks = objects_to_hyperlinks(obj.logs)
- return format_html(f'
')
+ try:
+ html_logs = format_html(
+ f''
+ )
+ except Exception as e:
+ html_logs = f"An error occurred while formatting the parsed logs as HTML. Exception {e}"
+ return html_logs
actions = [
"cancel_public_order",
diff --git a/api/utils.py b/api/utils.py
index 0eed88d9..85b5dbcf 100644
--- a/api/utils.py
+++ b/api/utils.py
@@ -488,11 +488,19 @@ def objects_to_hyperlinks(logs: str) -> str:
Used to format pretty logs for the Order admin panel.
"""
objects = ["LNPayment", "Robot", "Order", "OnchainPayment", "MarketTick"]
- for obj in objects:
- logs = re.sub(
- rf"{obj}\(([0-9a-fA-F\-A-F]+),\s*([^)]+)\)",
- lambda m: f'{m.group(2)}',
- logs,
- flags=re.DOTALL,
- )
+ try:
+ for obj in objects:
+ logs = re.sub(
+ rf"{obj}\(([0-9a-fA-F\-A-F]+),\s*([^)]+)\)",
+ lambda m: f'{m.group(2)}',
+ logs,
+ flags=re.DOTALL,
+ )
+
+ except re.error as e:
+ print("Error occurred:", e.msg)
+ print("Pattern:", e.pattern)
+ print("Position:", e.pos)
+ logs = f"An error occurred while parsing the logs. Exception {e}"
+
return logs
diff --git a/docs/assets/schemas/api-latest.yaml b/docs/assets/schemas/api-latest.yaml
index 055f0434..ebb473f4 100644
--- a/docs/assets/schemas/api-latest.yaml
+++ b/docs/assets/schemas/api-latest.yaml
@@ -1,7 +1,7 @@
openapi: 3.0.3
info:
title: RoboSats REST API
- version: 0.5.3
+ version: 0.5.4
x-logo:
url: https://raw.githubusercontent.com/Reckless-Satoshi/robosats/main/frontend/static/assets/images/robosats-0.1.1-banner.png
backgroundColor: '#FFFFFF'
diff --git a/tests/test_trade_pipeline.py b/tests/test_trade_pipeline.py
index a3671d50..4205b98c 100644
--- a/tests/test_trade_pipeline.py
+++ b/tests/test_trade_pipeline.py
@@ -7,6 +7,7 @@ from django.urls import reverse
from api.models import Currency, Order
from api.tasks import cache_market
+from django.contrib.admin.sites import AdminSite
from control.models import BalanceLog
from control.tasks import compute_node_balance, do_accounting
from tests.test_api import BaseAPITestCase
@@ -14,6 +15,8 @@ from tests.utils.node import add_invoice, set_up_regtest_network
from tests.utils.pgp import sign_message
from tests.utils.trade import Trade
+from api.admin import OrderAdmin
+
def read_file(file_path):
"""
@@ -44,6 +47,15 @@ class TradeTest(BaseAPITestCase):
# Take the first node balances snapshot
compute_node_balance()
+ def assert_order_logs(self, order_id):
+ order = Order.objects.get(id=order_id)
+ order_admin = OrderAdmin(model=Order, admin_site=AdminSite())
+ try:
+ result = order_admin._logs(order)
+ self.assertIsInstance(result, str)
+ except Exception as e:
+ self.fail(f"Exception occurred: {e}")
+
def test_login_superuser(self):
"""
Test the login functionality for the superuser.
@@ -225,6 +237,7 @@ class TradeTest(BaseAPITestCase):
data["satoshis"], "Relative pricing order has non-null Satoshis"
)
self.assertIsNone(data["taker"], "New order's taker is not null")
+ self.assert_order_logs(data["id"])
def test_get_order_created(self):
"""
@@ -270,6 +283,8 @@ class TradeTest(BaseAPITestCase):
# Cancel order to avoid leaving pending HTLCs after a successful test
trade.cancel_order()
+ self.assert_order_logs(data["id"])
+
def test_publish_order(self):
"""
Tests a trade from order creation to published (maker bond locked).
@@ -298,6 +313,8 @@ class TradeTest(BaseAPITestCase):
# Cancel order to avoid leaving pending HTLCs after a successful test
trade.cancel_order()
+ self.assert_order_logs(data["id"])
+
def test_pause_unpause_order(self):
"""
Tests pausing and unpausing a public order
@@ -323,6 +340,8 @@ class TradeTest(BaseAPITestCase):
# Cancel order to avoid leaving pending HTLCs after a successful test
trade.cancel_order()
+ self.assert_order_logs(data["id"])
+
def test_make_and_take_order(self):
"""
Tests a trade from order creation to taken.
@@ -362,6 +381,8 @@ class TradeTest(BaseAPITestCase):
# Cancel order to avoid leaving pending HTLCs after a successful test
trade.cancel_order()
+ self.assert_order_logs(data["id"])
+
def test_make_and_lock_contract(self):
"""
Tests a trade from order creation to taker bond locked.
@@ -407,6 +428,8 @@ class TradeTest(BaseAPITestCase):
# Maker cancels order to avoid leaving pending HTLCs after a successful test
trade.cancel_order()
+ self.assert_order_logs(data["id"])
+
def test_trade_to_locked_escrow(self):
"""
Tests a trade from order creation until escrow locked, before
@@ -455,6 +478,8 @@ class TradeTest(BaseAPITestCase):
trade.cancel_order(trade.maker_index)
trade.cancel_order(trade.taker_index)
+ self.assert_order_logs(data["id"])
+
def test_trade_to_submitted_invoice(self):
"""
Tests a trade from order creation until escrow locked and
@@ -509,6 +534,8 @@ class TradeTest(BaseAPITestCase):
trade.cancel_order(trade.maker_index)
trade.cancel_order(trade.taker_index)
+ self.assert_order_logs(data["id"])
+
def test_trade_to_confirm_fiat_received_LN(self):
"""
Tests a trade from order creation until fiat received is confirmed by seller/taker
@@ -534,6 +561,8 @@ class TradeTest(BaseAPITestCase):
self.assertFalse(data["taker_locked"])
self.assertFalse(data["escrow_locked"])
+ self.assert_order_logs(data["id"])
+
def test_successful_LN(self):
"""
Tests a trade from order creation until Sats sent to buyer
@@ -561,6 +590,8 @@ class TradeTest(BaseAPITestCase):
self.assertIsHash(data["maker_summary"]["preimage"])
self.assertIsHash(data["maker_summary"]["payment_hash"])
+ self.assert_order_logs(data["id"])
+
def test_successful_onchain(self):
"""
Tests a trade from order creation until Sats sent to buyer
@@ -588,6 +619,8 @@ class TradeTest(BaseAPITestCase):
self.assertIsInstance(data["maker_summary"]["address"], str)
self.assertIsHash(data["maker_summary"]["txid"])
+ self.assert_order_logs(data["id"])
+
def test_cancel_public_order(self):
"""
Tests the cancellation of a public order
@@ -667,6 +700,8 @@ class TradeTest(BaseAPITestCase):
)
self.assertEqual(data["expiry_reason"], Order.ExpiryReasons.NMBOND)
+ self.assert_order_logs(data["id"])
+
def test_public_order_expires(self):
"""
Tests the expiration of a public order
@@ -698,6 +733,8 @@ class TradeTest(BaseAPITestCase):
)
self.assertEqual(data["expiry_reason"], Order.ExpiryReasons.NTAKEN)
+ self.assert_order_logs(data["id"])
+
def test_taken_order_expires(self):
"""
Tests the expiration of a public order
@@ -731,6 +768,8 @@ class TradeTest(BaseAPITestCase):
)
self.assertEqual(data["expiry_reason"], Order.ExpiryReasons.NESINV)
+ self.assert_order_logs(data["id"])
+
def test_escrow_locked_expires(self):
"""
Tests the expiration of a public order
@@ -765,6 +804,8 @@ class TradeTest(BaseAPITestCase):
)
self.assertEqual(data["expiry_reason"], Order.ExpiryReasons.NINVOI)
+ self.assert_order_logs(data["id"])
+
def test_chat(self):
"""
Tests the chatting REST functionality
@@ -871,6 +912,8 @@ class TradeTest(BaseAPITestCase):
Order.Status.MLD,
)
+ self.assert_order_logs(data["id"])
+
def test_order_expires_after_only_maker_messaged(self):
"""
Tests the expiration of an order in chat where taker never messaged
@@ -911,6 +954,8 @@ class TradeTest(BaseAPITestCase):
Order.Status.TLD,
)
+ self.assert_order_logs(data["id"])
+
def test_withdraw_reward_after_unilateral_cancel(self):
"""
Tests withdraw rewards as taker after maker cancels order unilaterally
@@ -979,6 +1024,8 @@ class TradeTest(BaseAPITestCase):
Order.Status.DIS,
)
+ self.assert_order_logs(data["id"])
+
def test_ticks(self):
"""
Tests the historical ticks serving endpoint after creating a contract