feat: import 6 MCP services + common workspace
This commit is contained in:
@@ -0,0 +1,188 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from unittest.mock import AsyncMock, MagicMock
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
from mcp_deribit.server import create_app
|
||||
from option_mcp_common.auth import Principal, TokenStore
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_client():
|
||||
c = MagicMock()
|
||||
c.get_ticker = AsyncMock(return_value={"mark_price": 50000})
|
||||
c.get_instruments = AsyncMock(return_value=[])
|
||||
c.get_orderbook = AsyncMock(return_value={"bids": [], "asks": []})
|
||||
c.get_positions = AsyncMock(return_value=[])
|
||||
c.get_account_summary = AsyncMock(return_value={"equity": 1000})
|
||||
c.get_trade_history = AsyncMock(return_value=[])
|
||||
c.get_historical = AsyncMock(return_value={"candles": []})
|
||||
c.get_technical_indicators = AsyncMock(return_value={"rsi": 55.0})
|
||||
c.place_order = AsyncMock(return_value={"order_id": "x"})
|
||||
c.cancel_order = AsyncMock(return_value={"order_id": "x", "state": "cancelled"})
|
||||
c.set_stop_loss = AsyncMock(return_value={"order_id": "x", "stop_price": 45000})
|
||||
c.set_take_profit = AsyncMock(return_value={"order_id": "x", "tp_price": 55000})
|
||||
c.close_position = AsyncMock(return_value={"closed": True})
|
||||
c.set_leverage = AsyncMock(return_value={"state": "ok"})
|
||||
return c
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def http(mock_client):
|
||||
store = TokenStore(tokens={
|
||||
"ct": Principal("core", {"core"}),
|
||||
"ot": Principal("observer", {"observer"}),
|
||||
})
|
||||
app = create_app(client=mock_client, token_store=store)
|
||||
return TestClient(app)
|
||||
|
||||
|
||||
def test_health(http):
|
||||
assert http.get("/health").status_code == 200
|
||||
|
||||
|
||||
def test_get_ticker_core_ok(http):
|
||||
r = http.post(
|
||||
"/tools/get_ticker",
|
||||
headers={"Authorization": "Bearer ct"},
|
||||
json={"instrument_name": "BTC-PERPETUAL"},
|
||||
)
|
||||
assert r.status_code == 200
|
||||
assert r.json()["mark_price"] == 50000
|
||||
|
||||
|
||||
def test_get_ticker_observer_ok(http):
|
||||
r = http.post(
|
||||
"/tools/get_ticker",
|
||||
headers={"Authorization": "Bearer ot"},
|
||||
json={"instrument_name": "BTC-PERPETUAL"},
|
||||
)
|
||||
assert r.status_code == 200
|
||||
|
||||
|
||||
def test_get_ticker_no_auth_401(http):
|
||||
r = http.post("/tools/get_ticker", json={"instrument_name": "BTC-PERPETUAL"})
|
||||
assert r.status_code == 401
|
||||
|
||||
|
||||
def test_get_ticker_alias_instrument_ok(http, mock_client):
|
||||
r = http.post(
|
||||
"/tools/get_ticker",
|
||||
headers={"Authorization": "Bearer ct"},
|
||||
json={"instrument": "ETH"},
|
||||
)
|
||||
assert r.status_code == 200
|
||||
mock_client.get_ticker.assert_awaited_with("ETH")
|
||||
|
||||
|
||||
def test_place_order_core_ok(http):
|
||||
r = http.post(
|
||||
"/tools/place_order",
|
||||
headers={"Authorization": "Bearer ct"},
|
||||
json={"instrument_name": "BTC-PERPETUAL", "side": "buy", "amount": 10},
|
||||
)
|
||||
assert r.status_code == 200
|
||||
|
||||
|
||||
def test_place_order_observer_forbidden(http):
|
||||
r = http.post(
|
||||
"/tools/place_order",
|
||||
headers={"Authorization": "Bearer ot"},
|
||||
json={"instrument_name": "BTC-PERPETUAL", "side": "buy", "amount": 10},
|
||||
)
|
||||
assert r.status_code == 403
|
||||
|
||||
|
||||
def test_place_order_notional_cap_enforced(http):
|
||||
"""CER-016: reject se notional > CERBERO_MAX_NOTIONAL (default 200)."""
|
||||
r = http.post(
|
||||
"/tools/place_order",
|
||||
headers={"Authorization": "Bearer ct"},
|
||||
json={
|
||||
"instrument_name": "ETH-PERPETUAL",
|
||||
"side": "buy",
|
||||
"amount": 335, # USD — cap 200
|
||||
},
|
||||
)
|
||||
assert r.status_code == 403
|
||||
body = r.json()
|
||||
assert body["error"]["code"] == "HARD_PROHIBITION"
|
||||
|
||||
|
||||
def test_place_order_leverage_cap_enforced(http):
|
||||
"""CER-016: reject leverage > 3x."""
|
||||
r = http.post(
|
||||
"/tools/place_order",
|
||||
headers={"Authorization": "Bearer ct"},
|
||||
json={
|
||||
"instrument_name": "BTC-PERPETUAL",
|
||||
"side": "buy",
|
||||
"amount": 50,
|
||||
"leverage": 50,
|
||||
},
|
||||
)
|
||||
assert r.status_code == 403
|
||||
body = r.json()
|
||||
assert body["error"]["code"] == "HARD_PROHIBITION"
|
||||
|
||||
|
||||
def test_place_order_reduce_only_skips_cap(http):
|
||||
"""CER-016: reduce_only orders bypassano cap notional (è close)."""
|
||||
r = http.post(
|
||||
"/tools/place_order",
|
||||
headers={"Authorization": "Bearer ct"},
|
||||
json={
|
||||
"instrument_name": "ETH-PERPETUAL",
|
||||
"side": "sell",
|
||||
"amount": 10000,
|
||||
"reduce_only": True,
|
||||
},
|
||||
)
|
||||
assert r.status_code == 200
|
||||
|
||||
|
||||
def test_close_position_core_ok(http):
|
||||
r = http.post(
|
||||
"/tools/close_position",
|
||||
headers={"Authorization": "Bearer ct"},
|
||||
json={"instrument_name": "BTC-PERPETUAL"},
|
||||
)
|
||||
assert r.status_code == 200
|
||||
|
||||
|
||||
def test_close_position_observer_forbidden(http):
|
||||
r = http.post(
|
||||
"/tools/close_position",
|
||||
headers={"Authorization": "Bearer ot"},
|
||||
json={"instrument_name": "BTC-PERPETUAL"},
|
||||
)
|
||||
assert r.status_code == 403
|
||||
|
||||
|
||||
def test_cancel_order_observer_forbidden(http):
|
||||
r = http.post(
|
||||
"/tools/cancel_order",
|
||||
headers={"Authorization": "Bearer ot"},
|
||||
json={"order_id": "abc123"},
|
||||
)
|
||||
assert r.status_code == 403
|
||||
|
||||
|
||||
def test_set_stop_loss_observer_forbidden(http):
|
||||
r = http.post(
|
||||
"/tools/set_stop_loss",
|
||||
headers={"Authorization": "Bearer ot"},
|
||||
json={"order_id": "abc123", "stop_price": 45000.0},
|
||||
)
|
||||
assert r.status_code == 403
|
||||
|
||||
|
||||
def test_get_account_summary_observer_ok(http):
|
||||
r = http.post(
|
||||
"/tools/get_account_summary",
|
||||
headers={"Authorization": "Bearer ot"},
|
||||
json={"currency": "USDC"},
|
||||
)
|
||||
assert r.status_code == 200
|
||||
assert r.json()["equity"] == 1000
|
||||
Reference in New Issue
Block a user