Bybit MCP Server
by sammcj
- src
- __tests__
import { describe, beforeAll, it, expect } from '@jest/globals'
import { config } from 'dotenv'
import { join } from 'path'
import { existsSync } from 'fs'
import GetTicker from '../tools/GetTicker.js'
import GetOrderbook from '../tools/GetOrderbook.js'
import GetPositions from '../tools/GetPositions.js'
import GetWalletBalance from '../tools/GetWalletBalance.js'
import GetInstrumentInfo from '../tools/GetInstrumentInfo.js'
import GetKline from '../tools/GetKline.js'
import GetMarketInfo from '../tools/GetMarketInfo.js'
import GetOrderHistory from '../tools/GetOrderHistory.js'
import GetTrades from '../tools/GetTrades.js'
import { CallToolRequestSchema } from "@modelcontextprotocol/sdk/types.js"
import { z } from "zod"
type ToolCallRequest = z.infer<typeof CallToolRequestSchema>
// Load environment variables
const envPath = join(process.cwd(), '.env')
if (existsSync(envPath)) {
config({ path: envPath })
}
// Check if we're in development mode (no API credentials)
const isDevMode = !process.env.BYBIT_API_KEY || !process.env.BYBIT_API_SECRET
const useTestnet = process.env.BYBIT_USE_TESTNET === "true"
if (isDevMode) {
console.warn('Running in development mode with limited functionality')
}
describe('Bybit API Integration Tests', () => {
// Common test symbols
const testSymbols = {
spot: 'BTCUSDT',
linear: 'BTCUSDT',
inverse: 'BTCUSD'
}
beforeAll(() => {
if (isDevMode) {
console.warn('Running integration tests in development mode (testnet)')
} else {
console.info(`Running integration tests against ${useTestnet ? 'testnet' : 'mainnet'}`)
}
})
describe('Market Data Endpoints', () => {
describe('GetTicker', () => {
it('should fetch ticker data for spot market', async () => {
const getTicker = new GetTicker()
const request: ToolCallRequest = {
params: {
name: 'get_ticker',
arguments: {
category: 'spot',
symbol: testSymbols.spot
}
},
method: 'tools/call' as const
}
const result = await getTicker.toolCall(request)
expect(result.content[0].type).toBe('text')
const data = JSON.parse(result.content[0].text as string)
expect(data).toHaveProperty('symbol', testSymbols.spot)
expect(data).toHaveProperty('lastPrice')
})
})
describe('GetOrderbook', () => {
it('should fetch orderbook data for spot market', async () => {
const getOrderbook = new GetOrderbook()
const request: ToolCallRequest = {
params: {
name: 'get_orderbook',
arguments: {
category: 'spot',
symbol: testSymbols.spot,
limit: 5
}
},
method: 'tools/call' as const
}
const result = await getOrderbook.toolCall(request)
expect(result.content[0].type).toBe('text')
const data = JSON.parse(result.content[0].text as string)
expect(data).toHaveProperty('asks')
expect(data).toHaveProperty('bids')
})
})
describe('GetKline', () => {
it('should fetch kline data for spot market', async () => {
const getKline = new GetKline()
const request: ToolCallRequest = {
params: {
name: 'get_kline',
arguments: {
category: 'spot',
symbol: testSymbols.spot,
interval: '1',
limit: 5
}
},
method: 'tools/call' as const
}
const result = await getKline.toolCall(request)
expect(result.content[0].type).toBe('text')
const data = JSON.parse(result.content[0].text as string)
expect(Array.isArray(data)).toBe(true)
expect(data.length).toBeGreaterThan(0)
})
})
describe('GetTrades', () => {
it('should fetch recent trades for spot market', async () => {
const getTrades = new GetTrades()
const request: ToolCallRequest = {
params: {
name: 'get_trades',
arguments: {
category: 'spot',
symbol: testSymbols.spot,
limit: 5
}
},
method: 'tools/call' as const
}
const result = await getTrades.toolCall(request)
expect(result.content[0].type).toBe('text')
const data = JSON.parse(result.content[0].text as string)
expect(Array.isArray(data)).toBe(true)
expect(data.length).toBeGreaterThan(0)
})
})
});
// Skip account-specific tests in development mode
(isDevMode ? describe.skip : describe)('Account Data Endpoints', () => {
describe('GetPositions', () => {
it('should fetch positions for linear perpetual', async () => {
const getPositions = new GetPositions()
const request: ToolCallRequest = {
params: {
name: 'get_positions',
arguments: {
category: 'linear',
symbol: testSymbols.linear
}
},
method: 'tools/call' as const
}
const result = await getPositions.toolCall(request)
expect(result.content[0].type).toBe('text')
const data = JSON.parse(result.content[0].text as string)
expect(Array.isArray(data)).toBe(true)
})
})
describe('GetWalletBalance', () => {
it('should fetch wallet balance for unified account', async () => {
const getWalletBalance = new GetWalletBalance()
const request: ToolCallRequest = {
params: {
name: 'get_wallet_balance',
arguments: {
accountType: 'UNIFIED'
}
},
method: 'tools/call' as const
}
const result = await getWalletBalance.toolCall(request)
expect(result.content[0].type).toBe('text')
const data = JSON.parse(result.content[0].text as string)
expect(data).toHaveProperty('totalEquity')
expect(data).toHaveProperty('totalWalletBalance')
})
})
describe('GetOrderHistory', () => {
it('should fetch order history for spot market', async () => {
const getOrderHistory = new GetOrderHistory()
const request: ToolCallRequest = {
params: {
name: 'get_order_history',
arguments: {
category: 'spot',
limit: 5
}
},
method: 'tools/call' as const
}
const result = await getOrderHistory.toolCall(request)
expect(result.content[0].type).toBe('text')
const data = JSON.parse(result.content[0].text as string)
expect(Array.isArray(data)).toBe(true)
})
})
})
describe('Market Information Endpoints', () => {
describe('GetInstrumentInfo', () => {
it('should fetch instrument info for spot market', async () => {
const getInstrumentInfo = new GetInstrumentInfo()
const request: ToolCallRequest = {
params: {
name: 'get_instrument_info',
arguments: {
category: 'spot',
symbol: testSymbols.spot
}
},
method: 'tools/call' as const
}
const result = await getInstrumentInfo.toolCall(request)
expect(result.content[0].type).toBe('text')
const data = JSON.parse(result.content[0].text as string)
expect(data).toHaveProperty('symbol', testSymbols.spot)
})
})
describe('GetMarketInfo', () => {
it('should fetch market info for spot category', async () => {
const getMarketInfo = new GetMarketInfo()
const request: ToolCallRequest = {
params: {
name: 'get_market_info',
arguments: {
category: 'spot'
}
},
method: 'tools/call' as const
}
const result = await getMarketInfo.toolCall(request)
expect(result.content[0].type).toBe('text')
const data = JSON.parse(result.content[0].text as string)
expect(Array.isArray(data)).toBe(true)
expect(data.length).toBeGreaterThan(0)
})
})
})
describe('Error Handling', () => {
it('should handle invalid symbols gracefully', async () => {
const getTicker = new GetTicker()
const request: ToolCallRequest = {
params: {
name: 'get_ticker',
arguments: {
category: 'spot',
symbol: 'INVALID-PAIR'
}
},
method: 'tools/call' as const
}
const result = await getTicker.toolCall(request)
expect(result.content[0].type).toBe('error')
})
it('should handle invalid categories gracefully', async () => {
const getMarketInfo = new GetMarketInfo()
const request: ToolCallRequest = {
params: {
name: 'get_market_info',
arguments: {
category: 'invalid-category' as any
}
},
method: 'tools/call' as const
}
const result = await getMarketInfo.toolCall(request)
expect(result.content[0].type).toBe('error')
})
})
})