Skip to content

Commit

Permalink
SQS-347 | integration, synthetic monitoring tests for active orders (#…
Browse files Browse the repository at this point in the history
…504) (#507)

* SQS-347 | integration, synthetic monitoring tests for active orders
query

Implements integration, synthetic monitoring tests for the for active orders
query. Additionally adds test file for locust.

(cherry picked from commit 9de1489)

Co-authored-by: Deividas Petraitis <[email protected]>
  • Loading branch information
mergify[bot] and deividaspetraitis authored Sep 10, 2024
1 parent d16acb9 commit ed58eb2
Show file tree
Hide file tree
Showing 7 changed files with 182 additions and 33 deletions.
2 changes: 1 addition & 1 deletion locust/locustfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
from token_prices import TokenPrices
from in_given_out import ExactAmountOutQuote
from out_given_in import ExactAmountInQuote
from orderbook_active_orders import OrderbookActiveOrders
from passthrough_portfolio_balances import PassthroughPortfolioBalances
from passthrough_orderbook_active_orders import PassthroughOrderbookActiveOrders
18 changes: 0 additions & 18 deletions locust/orderbook_active_orders.py

This file was deleted.

10 changes: 10 additions & 0 deletions locust/passthrough_orderbook_active_orders.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from locust import HttpUser, task

# burner address for the integration tests
addr = "osmo1jgz4xmaw9yk9pjxd4h8c2zs0r0vmgyn88s8t6l"


class PassthroughOrderbookActiveOrders(HttpUser):
@task
def passthrough_orderbook_active_orders(self):
self.client.get(f"/passthrough/active-orders?userOsmoAddress={addr}")
111 changes: 111 additions & 0 deletions tests/active_orderbook_orders_response.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
from decimal import Decimal
from typing import List
import time
from datetime import datetime


class LimitOrderAsset:
def __init__(self, symbol: str):
self.symbol = symbol

def validate(self):
# Ensure symbol is not empty
assert self.symbol, "Symbol must not be empty"

class LimitOrder:
def __init__(self, tick_id: int, order_id: int, order_direction: str, owner: str, quantity: str, etas: str,
claim_bounty: str, placed_quantity: str, placed_at: int, price: str, percentClaimed: str,
totalFilled: str, percentFilled: str, orderbookAddress: str, status: str, output: str,
quote_asset: dict, base_asset: dict):
self.tick_id = int(tick_id)
self.order_id = int(order_id)
self.order_direction = order_direction
self.owner = owner
self.quantity = Decimal(quantity)
self.etas = Decimal(etas)
self.claim_bounty = Decimal(claim_bounty)
self.placed_quantity = Decimal(placed_quantity)
self.placed_at = int(placed_at)
self.price = Decimal(price)
self.percent_claimed = Decimal(percentClaimed) # Changed variable name
self.total_filled = Decimal(totalFilled)
self.percent_filled = Decimal(percentFilled)
self.orderbook_address = orderbookAddress
self.status = status
self.output = Decimal(output)
self.quote_asset = LimitOrderAsset(**quote_asset)
self.base_asset = LimitOrderAsset(**base_asset)

def validate(self, owner_address=None):
# Check if order_id is non-negative
assert self.order_id >= 0, f"Order ID {self.order_id} cannot be negative"

# Check if order_direction is either "bid" or "ask"
assert self.order_direction in ['bid', 'ask'], f"Order direction {self.order_direction} must be 'bid' or 'ask'"

# Validate owner address (Osmosis address format)
assert self.owner == owner_address, f"Owner address {self.owner} is invalid"

# Check if quantity is non-negative
assert self.quantity > 0, f"Quantity {self.quantity} cannot be negative"

# Check if claim_bounty is non-negative
assert self.claim_bounty > 0, f"Claim bounty {self.claim_bounty} cannot be negative"

# Validate placed_quantity is non-negative
assert self.placed_quantity > 0, f"Placed quantity {self.placed_quantity} cannot be negative"

# Validate placed_at is a valid Unix timestamp
assert 0 <= self.placed_at <= int(time.time()), f"Placed_at timestamp {self.placed_at} is invalid"

# Check if price is positive
assert self.price > 0, f"Price {self.price} must be positive"

# Check if percent_claimed is between 0 and 100
assert 0 <= self.percent_claimed <= 100, f"Percent claimed {self.percent_claimed} must be between 0 and 100"

# Check if total_filled is non-negative
assert self.total_filled >= 0, f"Total filled {self.total_filled} cannot be negative"

# Check if percent_filled is between 0 and 100
assert 0 <= self.percent_filled <= 100, f"Percent filled {self.percent_filled} must be between 0 and 100"

# Ensure status is not empty
assert self.status, "Status must not be empty"

# Ensure orderbook_address is not empty
assert self.orderbook_address, "Orderbook address must not be empty"

# Check if output is non-negative
assert self.output >= 0, f"Output {self.output} cannot be negative"

# Validate quote_asset
self.quote_asset.validate()

# Validate base_asset
self.base_asset.validate()

@staticmethod
def _is_valid_unix_timestamp(timestamp):
try:
datetime.utcfromtimestamp(int(timestamp))
return True
except (ValueError, OverflowError):
return False


class OrderbookActiveOrdersResponse:
def __init__(self, orders: List[dict], is_best_effort: bool):
self.orders = [LimitOrder(**order) for order in orders]
self.is_best_effort = is_best_effort

def validate(self, owner_address):
# Validate each order
order_ids = set()
for order in self.orders:
order.validate(owner_address)

# Ensure order_id is unique
if order.order_id in order_ids:
raise ValueError(f"Duplicate order_id found: {order.order_id}")
order_ids.add(order.order_id)
14 changes: 14 additions & 0 deletions tests/sqs_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
CANONICAL_ORDERBOOKS_URL = "/pools/canonical-orderbooks"

PASSTHROUGH_PORTFOLIO_ASSETS = "/passthrough/portfolio-assets/"
PASSTHROUGH_ACTIVE_ORDERBOOK_ORDERS = "/passthrough/active-orders"

CONFIG_URL = "/config"

Expand Down Expand Up @@ -240,6 +241,19 @@ def get_canonical_orderbooks(self):

return response.json()


def get_active_orderbook_orders(self, address):
"""
Fetches active orderbook orders from the specified endpoint and address and returns them.
"""

response = requests.get(self.url + f"{PASSTHROUGH_ACTIVE_ORDERBOOK_ORDERS}?userOsmoAddress={address}", headers=self.headers)

if response.status_code != 200:
raise Exception(f"Error fetching active orderbook orders: {response.text}")

return response.json()

def get_portfolio_assets(self, address):
"""
Fetches the portfolio assets from the specified endpoint and address and returns them.
Expand Down
54 changes: 41 additions & 13 deletions tests/test_passthrough.py
Original file line number Diff line number Diff line change
@@ -1,34 +1,63 @@
import time
import conftest
import pytest

from sqs_service import *
import util
from active_orderbook_orders_response import OrderbookActiveOrdersResponse
from conftest import SERVICE_MAP
from e2e_math import *
from decimal import *

# Arbitrary choice based on performance at the time of test writing
EXPECTED_LATENCY_UPPER_BOUND_MS = 150

user_balances_assets_category_name = "user-balances"
unstaking_assets_category_name = "unstaking"
staked_assets_category_name = "staked"
inLocks_assets_category_name = "in-locks"
pooled_assets_category_name = "pooled"
unclaimed_rewards_assets_category_name = "unclaimed-rewards"
total_assets_category_name = "total-assets"
user_balances_assets_category_name = "user-balances"
unstaking_assets_category_name = "unstaking"
staked_assets_category_name = "staked"
inLocks_assets_category_name = "in-locks"
pooled_assets_category_name = "pooled"
unclaimed_rewards_assets_category_name = "unclaimed-rewards"
total_assets_category_name = "total-assets"

# Test suite for the /passthrough endpoint

# Note: this is for convinience to skip long-running tests in development
# locally.
# @pytest.mark.skip(reason="This test is currently disabled")
class TestPassthrough:


class TestPassthrough:
def test_poortfolio_assets(self, environment_url):
run_test_portfolio_assets(environment_url)

def test_active_orderbook_orders(self, environment_url):
run_test_active_orderbook_orders(environment_url)


def run_test_active_orderbook_orders(environment_url):
sqs_service = SERVICE_MAP[environment_url]

# list of burner addresses for the integration tests
addresses = [
"osmo1jgz4xmaw9yk9pjxd4h8c2zs0r0vmgyn88s8t6l",
]

for address in addresses:

start_time = time.time()
response = sqs_service.get_active_orderbook_orders(address)
elapsed_time_ms = (time.time() - start_time) * 1000

assert EXPECTED_LATENCY_UPPER_BOUND_MS > elapsed_time_ms, f"Error: latency {elapsed_time_ms} exceeded {EXPECTED_LATENCY_UPPER_BOUND_MS} ms"

resp = OrderbookActiveOrdersResponse(**response)

resp.validate(address)


def run_test_portfolio_assets(environment_url):
sqs_service = SERVICE_MAP[environment_url]

# Arbitrary addresses
addresses = [
"osmo1044qatzg4a0wm63jchrfdnn2u8nwdgxxt6e524",
Expand Down Expand Up @@ -64,6 +93,7 @@ def run_test_portfolio_assets(environment_url):
total_assets = categories.get(total_assets_category_name)
validate_category(total_assets, True)


def validate_category(category, should_have_breakdown=False):
assert category is not None

Expand All @@ -80,9 +110,7 @@ def validate_category(category, should_have_breakdown=False):

account_coins_result = category.get('account_coins_result')
assert account_coins_result is not None

for coin_result in account_coins_result:
assert coin_result.get('coin') is not None
assert coin_result.get('cap_value') is not None


6 changes: 5 additions & 1 deletion tests/test_synthetic_geo.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from util import *

from test_pools import run_pool_liquidity_cap_test, run_canonical_orderbook_test, run_pool_filters_test
from test_passthrough import run_test_portfolio_assets
from test_passthrough import run_test_portfolio_assets, run_test_active_orderbook_orders
from test_candidate_routes import run_candidate_routes_test
from test_router_quote_out_given_in import TestExactAmountInQuote
from test_router_quote_in_given_out import TestExactAmountOutQuote
Expand Down Expand Up @@ -41,6 +41,10 @@ def test_synth_pools_filters(self, environment_url):
def test_synth_passthrough_portfolio_assets(self, environment_url):
run_test_portfolio_assets(environment_url)

# /passthrough/active-orders endpoint
def test_synth_passthrough_active_orders(self, environment_url):
run_test_active_orderbook_orders(environment_url)

# /router/routes endpoint
def test_synth_candidate_routes(self, environment_url):
tokens_to_pair = [constants.USDC, constants.UOSMO]
Expand Down

0 comments on commit ed58eb2

Please sign in to comment.