TradingAgents/tests/test_oneinch_executor.py

133 lines
4.5 KiB
Python

import pytest
from unittest.mock import patch, AsyncMock, MagicMock
from tradingagents.execution.base_executor import TradeOrder
from tradingagents.execution.oneinch_executor import OneInchExecutor
@pytest.mark.asyncio
@patch("httpx.AsyncClient")
async def test_oneinch_get_quote_returns_valid_route(mock_async_client_class):
# Arrange
executor = OneInchExecutor(
"https://ethereum-rpc.publicnode.com",
"0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
1,
)
order = TradeOrder(
action="buy",
token_in="0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", # USDC
token_out="0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", # WETH
amount=100.0, # 100 USDC
slippage_bps=50,
chain="ethereum",
)
# Setup mock
mock_client = AsyncMock()
mock_response = MagicMock()
mock_response.json.return_value = {"dstAmount": "50000000000000000"} # 0.05 WETH
mock_client.get.return_value = mock_response
mock_async_client_class.return_value.__aenter__.return_value = mock_client
# Act
quote = await executor.get_quote(order)
# Assert
assert quote is not None
assert "dstAmount" in quote
# Verify the mock was called with correct parameters
mock_client.get.assert_called_once()
called_url, kwargs = mock_client.get.call_args
assert "https://api.1inch.dev/swap/v6.0/1/quote" in called_url[0]
assert kwargs["params"]["src"] == order.token_in
assert kwargs["params"]["dst"] == order.token_out
@pytest.mark.asyncio
@patch("httpx.AsyncClient")
@patch("web3.eth.async_eth.AsyncEth.send_raw_transaction")
async def test_oneinch_execute_swap_returns_success(
mock_send_raw, mock_async_client_class
):
# Arrange
executor = OneInchExecutor(
"https://ethereum-rpc.publicnode.com",
"0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
1,
)
order = TradeOrder(
action="buy",
token_in="0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", # USDC
token_out="0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", # WETH
amount=100.0, # 100 USDC
slippage_bps=50,
chain="ethereum",
)
# Setup API Mocks
mock_client = AsyncMock()
mock_quote_response = MagicMock()
mock_quote_response.json.return_value = {
"dstAmount": "50000000000000000"
} # mock quote
mock_swap_response = MagicMock()
# 1inch V6 API returns 'tx' with transaction data
mock_swap_response.json.return_value = {
"tx": {
"from": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
"to": "0xE592427A0AEce92De3Edee1F18E0157C05861564", # Uniswap V3 router example
"data": "0xabcdef",
"value": "0",
"gas": 100000,
"gasPrice": "20000000000",
}
}
# Side effect to return different responses for /quote and /swap
async def mock_get(url, **kwargs):
if "quote" in url:
return mock_quote_response
if "swap" in url:
return mock_swap_response
mock_client.get.side_effect = mock_get
mock_async_client_class.return_value.__aenter__.return_value = mock_client
# Setup Web3 RPC Mock
mock_send_raw.return_value = b"mock_tx_hash_123"
# Act
# We patch executor._confirm_and_parse since we don't need to test EVM confirmation loop here
with patch.object(
executor, "_confirm_and_parse", new_callable=AsyncMock
) as mock_confirm:
from tradingagents.execution.base_executor import TradeResult
mock_confirm.return_value = TradeResult(
success=True,
tx_hash="0xmock_tx_hash_123",
amount_in=100.0,
amount_out=0.05,
price_impact=0.1,
gas_cost=0.005,
timestamp="2024-01-01",
)
# Mock web3 transaction count & signing
with patch.object(
executor.w3.eth, "get_transaction_count", new_callable=AsyncMock
) as mock_tc:
mock_tc.return_value = 1
with patch.object(executor.account, "sign_transaction") as mock_sign:
mock_sign.return_value = MagicMock(raw_transaction=b"mock_raw_bytes")
result = await executor.execute_swap(order)
# Assert
assert result.success is True
assert result.tx_hash == "0xmock_tx_hash_123"
assert result.amount_in == 100.0
assert result.amount_out == 0.05
# 1 call for swap directly builds the tx
assert mock_client.get.call_count == 1