2023-11-06 14:01:56 +00:00
|
|
|
import json
|
2023-11-06 19:33:40 +00:00
|
|
|
from datetime import datetime
|
|
|
|
from decimal import Decimal
|
2023-11-06 14:01:56 +00:00
|
|
|
|
|
|
|
from decouple import config
|
|
|
|
from django.contrib.auth.models import User
|
|
|
|
from django.test import Client, TestCase
|
|
|
|
|
2023-11-06 19:33:40 +00:00
|
|
|
from api.models import Currency, Order
|
|
|
|
from api.tasks import cache_market
|
|
|
|
|
2023-11-06 14:01:56 +00:00
|
|
|
|
|
|
|
class TradeTest(TestCase):
|
|
|
|
su_pass = "12345678"
|
|
|
|
su_name = config("ESCROW_USERNAME", cast=str, default="admin")
|
|
|
|
|
|
|
|
def setUp(self):
|
2023-11-06 19:33:40 +00:00
|
|
|
"""
|
|
|
|
Create a superuser. The superuser is the escrow party.
|
|
|
|
"""
|
2023-11-06 14:01:56 +00:00
|
|
|
self.client = Client()
|
|
|
|
User.objects.create_superuser(self.su_name, "super@user.com", self.su_pass)
|
|
|
|
|
|
|
|
def test_login_superuser(self):
|
|
|
|
path = "/coordinator/login/"
|
|
|
|
data = {"username": self.su_name, "password": self.su_pass}
|
|
|
|
response = self.client.post(path, data)
|
|
|
|
self.assertEqual(response.status_code, 302)
|
|
|
|
|
2023-11-06 19:33:40 +00:00
|
|
|
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:
|
2023-11-06 14:01:56 +00:00
|
|
|
b91_token = file.read()
|
2023-11-06 19:33:40 +00:00
|
|
|
with open(f"tests/robots/{robot_index}/pub_key", "r") as file:
|
2023-11-06 14:01:56 +00:00
|
|
|
pub_key = file.read()
|
2023-11-06 19:33:40 +00:00
|
|
|
with open(f"tests/robots/{robot_index}/enc_priv_key", "r") as file:
|
2023-11-06 14:01:56 +00:00
|
|
|
enc_priv_key = file.read()
|
|
|
|
|
|
|
|
headers = {
|
|
|
|
"HTTP_AUTHORIZATION": f"Token {b91_token} | Public {pub_key} | Private {enc_priv_key}"
|
|
|
|
}
|
|
|
|
return headers, pub_key, enc_priv_key
|
|
|
|
|
2023-11-06 19:33:40 +00:00
|
|
|
def create_robot(self, robot_index):
|
2023-11-06 14:01:56 +00:00
|
|
|
"""
|
2023-11-06 19:33:40 +00:00
|
|
|
Creates the robots in /tests/robots/{robot_index}
|
2023-11-06 14:01:56 +00:00
|
|
|
"""
|
|
|
|
path = "/api/robot/"
|
2023-11-06 19:33:40 +00:00
|
|
|
headers, pub_key, enc_priv_key = self.get_robot_auth(robot_index)
|
2023-11-06 14:01:56 +00:00
|
|
|
|
|
|
|
response = self.client.get(path, **headers)
|
|
|
|
data = json.loads(response.content.decode())
|
|
|
|
|
2023-11-06 19:33:40 +00:00
|
|
|
with open(f"tests/robots/{robot_index}/nickname", "r") as file:
|
2023-11-06 14:01:56 +00:00
|
|
|
expected_nickname = file.read()
|
|
|
|
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
self.assertEqual(
|
|
|
|
data["nickname"],
|
|
|
|
expected_nickname,
|
2023-11-06 19:33:40 +00:00
|
|
|
f"Robot {robot_index} created nickname is not MyopicRacket333",
|
2023-11-06 14:01:56 +00:00
|
|
|
)
|
|
|
|
self.assertEqual(
|
|
|
|
data["public_key"], pub_key, "Returned public Kky does not match"
|
|
|
|
)
|
|
|
|
self.assertEqual(
|
|
|
|
data["encrypted_private_key"],
|
|
|
|
enc_priv_key,
|
|
|
|
"Returned encrypted private key does not match",
|
|
|
|
)
|
|
|
|
self.assertEqual(
|
|
|
|
len(data["tg_token"]), 15, "String is not exactly 15 characters long"
|
|
|
|
)
|
|
|
|
self.assertEqual(
|
|
|
|
data["tg_bot_name"],
|
|
|
|
config(
|
|
|
|
"TELEGRAM_BOT_NAME", cast=str, default="RoboCoordinatorNotificationBot"
|
|
|
|
),
|
|
|
|
"Telegram bot name is not correct",
|
|
|
|
)
|
|
|
|
self.assertFalse(
|
|
|
|
data["tg_enabled"], "The new robot's telegram seems to be enabled"
|
|
|
|
)
|
|
|
|
self.assertEqual(data["earned_rewards"], 0, "The new robot's rewards are not 0")
|
|
|
|
|
|
|
|
def test_create_robots(self):
|
|
|
|
"""
|
2023-11-06 19:33:40 +00:00
|
|
|
Creates two robots to be used in the trade tests
|
2023-11-06 14:01:56 +00:00
|
|
|
"""
|
2023-11-06 19:33:40 +00:00
|
|
|
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}",
|
|
|
|
)
|