"""Tests for HyperliquidClient.""" from __future__ import annotations from decimal import Decimal import pytest from pytest_httpx import HTTPXMock from cerbero_bite.clients._base import HttpToolClient from cerbero_bite.clients._exceptions import McpDataAnomalyError from cerbero_bite.clients.hyperliquid import ( HOURLY_FUNDING_PERIODS_PER_YEAR, HyperliquidClient, ) def _client() -> HyperliquidClient: http = HttpToolClient( service="hyperliquid", base_url="http://mcp-hyperliquid:9012", token="t", retry_max=1, ) return HyperliquidClient(http) @pytest.mark.asyncio async def test_funding_rate_annualized(httpx_mock: HTTPXMock) -> None: httpx_mock.add_response( url="http://mcp-hyperliquid:9012/tools/get_funding_rate", json={"asset": "ETH", "current_funding_rate": 0.00005}, ) out = await _client().funding_rate_annualized("eth") assert out == Decimal("0.00005") * Decimal(HOURLY_FUNDING_PERIODS_PER_YEAR) @pytest.mark.asyncio async def test_funding_rate_anomaly_on_error_envelope(httpx_mock: HTTPXMock) -> None: httpx_mock.add_response(json={"error": "Asset XYZ not found"}) with pytest.raises(McpDataAnomalyError, match="Asset XYZ"): await _client().funding_rate_annualized("XYZ") @pytest.mark.asyncio async def test_funding_rate_anomaly_when_rate_missing(httpx_mock: HTTPXMock) -> None: httpx_mock.add_response(json={"asset": "ETH"}) with pytest.raises(McpDataAnomalyError, match="current_funding_rate"): await _client().funding_rate_annualized("ETH") def test_hyperliquid_client_rejects_wrong_service() -> None: bad = HttpToolClient( service="macro", base_url="http://x:1", token="t", retry_max=1 ) with pytest.raises(ValueError, match="requires service 'hyperliquid'"): HyperliquidClient(bad)