from __future__ import annotations from unittest.mock import AsyncMock, MagicMock import pytest from cerbero_mcp.exchanges.ibkr import tools as t def test_place_order_req_schema(): req = t.PlaceOrderReq(symbol="AAPL", side="buy", qty=1) assert req.order_type == "market" assert req.tif == "day" assert req.exchange == "SMART" def test_place_order_req_options_validates_occ(): req = t.PlaceOrderReq( symbol="AAPL 240119C00190000", side="buy", qty=1, asset_class="options", ) assert req.asset_class == "options" @pytest.mark.asyncio async def test_get_account_tool_calls_client(): client = MagicMock() client.get_account = AsyncMock(return_value={"netliquidation": {"amount": 10000}}) res = await t.get_account(client, t.GetAccountReq()) assert res["netliquidation"]["amount"] == 10000 @pytest.mark.asyncio async def test_get_tick_uses_cache_or_subscribes(): client = MagicMock() client.resolve_conid = AsyncMock(return_value=42) ws = MagicMock() ws.get_tick_snapshot = MagicMock(side_effect=[ None, {"conid": 42, "last_price": 99.5, "bid": 99.4, "ask": 99.6, "bid_size": 1, "ask_size": 1, "timestamp_ms": 1700000000000}, ]) ws.subscribe_tick = AsyncMock() res = await t.get_tick( client, t.GetTickReq(symbol="AAPL"), ws=ws, timeout_s=0.05, ) assert res["last_price"] == 99.5 ws.subscribe_tick.assert_awaited_once_with(42) @pytest.mark.asyncio async def test_place_order_enforces_leverage(): client = MagicMock() client.get_account = AsyncMock(return_value={ "netliquidation": {"amount": 10000}, }) client.place_order = AsyncMock(return_value={"order_id": "O1"}) creds = {"max_leverage": 2} res = await t.place_order( client, t.PlaceOrderReq(symbol="AAPL", side="buy", qty=10), creds=creds, last_price=100.0, ) assert res["order_id"] == "O1" @pytest.mark.asyncio async def test_cancel_order_calls_client(): client = MagicMock() client.cancel_order = AsyncMock(return_value={"order_id": "O1", "canceled": True}) res = await t.cancel_order(client, t.CancelOrderReq(order_id="O1")) assert res["canceled"] is True @pytest.mark.asyncio async def test_place_order_rejects_excessive_leverage(): from fastapi import HTTPException client = MagicMock() client.get_account = AsyncMock(return_value={ "netliquidation": {"amount": 1000}, }) creds = {"max_leverage": 2} # Order notional = 100*100 = 10000 vs equity 1000 → ratio 10x >> 2x cap → 403 with pytest.raises(HTTPException) as exc_info: await t.place_order( client, t.PlaceOrderReq(symbol="AAPL", side="buy", qty=100), creds=creds, last_price=100.0, ) assert exc_info.value.status_code == 403 assert exc_info.value.detail["error"] == "LEVERAGE_CAP_EXCEEDED"