From 8808ee85aee6f224f21c6f3e879eddd4c2a21712 Mon Sep 17 00:00:00 2001 From: Reckless_Satoshi Date: Mon, 6 Nov 2023 19:33:40 +0000 Subject: [PATCH] Add order creation tests --- api/views.py | 2 - tests/robots/2/b91_token | 1 + tests/robots/2/enc_priv_key | 18 ++++ tests/robots/2/nickname | 1 + tests/robots/2/pub_key | 14 ++++ tests/robots/2/token | 0 tests/test_trade_pipeline.py | 157 ++++++++++++++++++++++++++++++++--- 7 files changed, 179 insertions(+), 14 deletions(-) create mode 100644 tests/robots/2/b91_token create mode 100644 tests/robots/2/enc_priv_key create mode 100644 tests/robots/2/nickname create mode 100644 tests/robots/2/pub_key create mode 100644 tests/robots/2/token diff --git a/api/views.py b/api/views.py index d03485ef..2239d18a 100644 --- a/api/views.py +++ b/api/views.py @@ -118,8 +118,6 @@ class MakerView(CreateAPIView): if has_range is None: has_range = False - # TODO add a check - if `is_explicit` is true then `satoshis` need to be specified - # An order can either have an amount or a range (min_amount and max_amount) if has_range: amount = None diff --git a/tests/robots/2/b91_token b/tests/robots/2/b91_token new file mode 100644 index 00000000..c13f6315 --- /dev/null +++ b/tests/robots/2/b91_token @@ -0,0 +1 @@ +ltdCXV@ZH=5aR;^,"gQHJ*S#|o/H,RD;:m^>0p[D \ No newline at end of file diff --git a/tests/robots/2/enc_priv_key b/tests/robots/2/enc_priv_key new file mode 100644 index 00000000..6310d699 --- /dev/null +++ b/tests/robots/2/enc_priv_key @@ -0,0 +1,18 @@ +-----BEGIN PGP PRIVATE KEY BLOCK----- + +xYYEZUepOxYJKwYBBAHaRw8BAQdAM/asZMBNC2n1cNL7nQjfF6D5pjaQ4JyX +ChldvSOxF6T+CQMIydehiycpJtLgL1oH/9M+VBYWfRk0pxm4ut/UCArptMAA +myrAl7drgV44g1XNMZSNh8z4VeJ+9NB59VNpf+XETgQHDxueBHQDs8gP0Guw +OM1MUm9ib1NhdHMgSUQgZTRjMGQ0MDE0ZmY3Y2E5MGUyZWI2Yjg4OTg3YTdm +OTg2NWMzYjViMTFjY2QxMTkxYjZjZmJkN2FmYzQ4ZjFhMsKMBBAWCgA+BYJl +R6k7BAsJBwgJkF6N1APT+xgtAxUICgQWAAIBAhkBApsDAh4BFiEE6qRwE6NG +7J8cgwUnXo3UA9P7GC0AALfZAP9UKi14KtDPz7E41diuyxbUux339WxzSWDW +qZKLXXhmpwEAmaeYM682fqcugFWhaqybLmJZJxr79OISluA2op5EBQ7HiwRl +R6k7EgorBgEEAZdVAQUBAQdAau/drRt10gnaO2365mKJpLIyjERKu+GlAfPV +tBXeaSUDAQgH/gkDCN8Sih6mM3/14KSBHq833UnFq0ZCR9al7aFSxv4R0XR+ +2/bnXwwA2OnluYYuBDWM76AuFMbtWM26PDXEHnqypjK62ys9lkYaLmW1cqnn +SZHCeAQYFggAKgWCZUepOwmQXo3UA9P7GC0CmwwWIQTqpHATo0bsnxyDBSde +jdQD0/sYLQAAX3oBAO31udzR6wTfMOTH3HRpVe1vC/vAPvmsvDEITsrSLQtR +APoChbViJLFxeA9c5b8inUCG9jgAfqeIevoKDaDS1O2EDw== +=3D/z +-----END PGP PRIVATE KEY BLOCK----- \ No newline at end of file diff --git a/tests/robots/2/nickname b/tests/robots/2/nickname new file mode 100644 index 00000000..a77568c4 --- /dev/null +++ b/tests/robots/2/nickname @@ -0,0 +1 @@ +EquivalentWool707 \ No newline at end of file diff --git a/tests/robots/2/pub_key b/tests/robots/2/pub_key new file mode 100644 index 00000000..1e2174df --- /dev/null +++ b/tests/robots/2/pub_key @@ -0,0 +1,14 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mDMEZUepOxYJKwYBBAHaRw8BAQdAM/asZMBNC2n1cNL7nQjfF6D5pjaQ4JyXChld +vSOxF6S0TFJvYm9TYXRzIElEIGU0YzBkNDAxNGZmN2NhOTBlMmViNmI4ODk4N2E3 +Zjk4NjVjM2I1YjExY2NkMTE5MWI2Y2ZiZDdhZmM0OGYxYTKIjAQQFgoAPgWCZUep +OwQLCQcICZBejdQD0/sYLQMVCAoEFgACAQIZAQKbAwIeARYhBOqkcBOjRuyfHIMF +J16N1APT+xgtAAC32QD/VCoteCrQz8+xONXYrssW1Lsd9/Vsc0lg1qmSi114ZqcB +AJmnmDOvNn6nLoBVoWqsmy5iWSca+/TiEpbgNqKeRAUOuDgEZUepOxIKKwYBBAGX +VQEFAQEHQGrv3a0bddIJ2jtt+uZiiaSyMoxESrvhpQHz1bQV3mklAwEIB4h4BBgW +CAAqBYJlR6k7CZBejdQD0/sYLQKbDBYhBOqkcBOjRuyfHIMFJ16N1APT+xgtAABf +egEA7fW53NHrBN8w5MfcdGlV7W8L+8A++ay8MQhOytItC1EA+gKFtWIksXF4D1zl +vyKdQIb2OAB+p4h6+goNoNLU7YQP +=8g49 +-----END PGP PUBLIC KEY BLOCK----- diff --git a/tests/robots/2/token b/tests/robots/2/token new file mode 100644 index 00000000..e69de29b diff --git a/tests/test_trade_pipeline.py b/tests/test_trade_pipeline.py index 99720b08..75553421 100644 --- a/tests/test_trade_pipeline.py +++ b/tests/test_trade_pipeline.py @@ -1,18 +1,25 @@ import json +from datetime import datetime +from decimal import Decimal from decouple import config from django.contrib.auth.models import User from django.test import Client, TestCase +from api.models import Currency, Order +from api.tasks import cache_market + class TradeTest(TestCase): su_pass = "12345678" su_name = config("ESCROW_USERNAME", cast=str, default="admin") def setUp(self): + """ + Create a superuser. The superuser is the escrow party. + """ self.client = Client() User.objects.create_superuser(self.su_name, "super@user.com", self.su_pass) - print("Super user created") def test_login_superuser(self): path = "/coordinator/login/" @@ -20,12 +27,16 @@ class TradeTest(TestCase): response = self.client.post(path, data) self.assertEqual(response.status_code, 302) - def get_robot_auth(self, index): - with open(f"tests/robots/{index}/b91_token", "r") as file: + def get_robot_auth(self, robot_index): + """ + Crates an AUTH header that embeds token, pub_key and enc_priv_key into a single string + just as requested by the robosats token middleware. + """ + with open(f"tests/robots/{robot_index}/b91_token", "r") as file: b91_token = file.read() - with open(f"tests/robots/{index}/pub_key", "r") as file: + with open(f"tests/robots/{robot_index}/pub_key", "r") as file: pub_key = file.read() - with open(f"tests/robots/{index}/enc_priv_key", "r") as file: + with open(f"tests/robots/{robot_index}/enc_priv_key", "r") as file: enc_priv_key = file.read() headers = { @@ -33,24 +44,24 @@ class TradeTest(TestCase): } return headers, pub_key, enc_priv_key - def create_robot(self, index): + def create_robot(self, robot_index): """ - Creates the robots in /tests/robots/{index}/ + Creates the robots in /tests/robots/{robot_index} """ path = "/api/robot/" - headers, pub_key, enc_priv_key = self.get_robot_auth(index) + headers, pub_key, enc_priv_key = self.get_robot_auth(robot_index) response = self.client.get(path, **headers) data = json.loads(response.content.decode()) - with open(f"tests/robots/{index}/nickname", "r") as file: + with open(f"tests/robots/{robot_index}/nickname", "r") as file: expected_nickname = file.read() self.assertEqual(response.status_code, 200) self.assertEqual( data["nickname"], expected_nickname, - f"Robot {index} created nickname is not MyopicRacket333", + f"Robot {robot_index} created nickname is not MyopicRacket333", ) self.assertEqual( data["public_key"], pub_key, "Returned public Kky does not match" @@ -77,6 +88,128 @@ class TradeTest(TestCase): def test_create_robots(self): """ - Creates two robots to test trades + Creates two robots to be used in the trade tests """ - self.create_robot(index=1) + self.create_robot(robot_index=1) + self.create_robot(robot_index=2) + + def test_cache_market(self): + cache_market() + + usd = Currency.objects.get(id=1) + self.assertTrue( + isinstance(usd.exchange_rate, Decimal), "Exchange rate is not decimal" + ) + self.assertLess(0, usd.exchange_rate, "Exchange rate is not higher than zero") + self.assertTrue( + isinstance(usd.timestamp, datetime), + "Externa price timestamp is not datetime", + ) + + def test_create_order( + self, + robot_index=1, + payment_method="Advcash Cash F2F", + min_amount=21, + max_amount=101.7, + premium=3.34, + public_duration=69360, + escrow_duration=8700, + bond_size=3.5, + latitude=34.7455, + longitude=135.503, + ): + # Requisites + # Cache market prices + self.test_cache_market() + path = "/api/make/" + # Get valid robot auth headers + headers, _, _ = self.get_robot_auth(robot_index) + + # Prepare request body + maker_form = { + "type": Order.Types.BUY, + "currency": 1, + "has_range": True, + "min_amount": min_amount, + "max_amount": max_amount, + "payment_method": payment_method, + "is_explicit": False, + "premium": premium, + "public_duration": public_duration, + "escrow_duration": escrow_duration, + "bond_size": bond_size, + "latitude": latitude, + "longitude": longitude, + } + + response = self.client.post(path, maker_form, **headers) + data = json.loads(response.content.decode()) + + # Checks + self.assertTrue(isinstance(data["id"], int), "Order ID is not an integer") + self.assertEqual( + data["status"], + Order.Status.WFB, + "Newly created order status is not 'Waiting for maker bond'", + ) + self.assertTrue( + isinstance(datetime.fromisoformat(data["created_at"]), datetime), + "Order creation timestamp is not datetime", + ) + self.assertTrue( + isinstance(datetime.fromisoformat(data["expires_at"]), datetime), + "Order expiry time is not datetime", + ) + self.assertEqual( + data["type"], Order.Types.BUY, "Buy order is not of type value BUY" + ) + self.assertEqual(data["currency"], 1, "Order for USD is not of currency USD") + self.assertIsNone( + 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.assertEqual( + float(data["min_amount"]), min_amount, "Order min amount does not match" + ) + self.assertEqual( + float(data["max_amount"]), max_amount, "Order max amount does not match" + ) + self.assertEqual( + data["payment_method"], + payment_method, + "Order payment method does not match", + ) + self.assertEqual( + data["escrow_duration"], + escrow_duration, + "Order escrow duration does not match", + ) + self.assertEqual( + float(data["bond_size"]), bond_size, "Order bond size does not match" + ) + self.assertEqual( + float(data["latitude"]), latitude, "Order latitude does not match" + ) + self.assertEqual( + float(data["longitude"]), longitude, "Order longitude does not match" + ) + self.assertEqual( + float(data["premium"]), premium, "Order premium does not match" + ) + self.assertFalse( + data["is_explicit"], "Relative pricing order has True is_explicit" + ) + self.assertIsNone( + data["satoshis"], "Relative pricing order has non null Satoshis" + ) + self.assertIsNone(data["taker"], "New order's taker is not null") + + with open(f"tests/robots/{robot_index}/nickname", "r") as file: + maker_nickname = file.read() + maker_id = User.objects.get(username=maker_nickname).id + self.assertEqual( + data["maker"], + maker_id, + "Maker user ID is not that of robot index {robot_index}", + )