Files
trading_bot_v4/tests/integration/orders/exit-orders-validation.test.ts
2025-12-10 15:05:44 +01:00

186 lines
5.6 KiB
TypeScript

/**
* Bug #76 Test: Stop-Loss Placement Validation
*
* Tests that placeExitOrders returns failure when SL signature is missing
* and logs critical errors appropriately.
*
* Created: Dec 9, 2025
* Reason: Bug #76 - placeExitOrders() can return SUCCESS with missing SL orders
*/
import { placeExitOrders, PlaceExitOrdersOptions } from '../../../lib/drift/orders'
// Mock dependencies
jest.mock('../../../lib/drift/client')
jest.mock('../../../lib/utils/logger')
jest.mock('../../../config/trading')
describe('Bug #76: Exit Orders Validation', () => {
let mockDriftClient: any
beforeEach(() => {
jest.clearAllMocks()
// Mock Drift service and client
mockDriftClient = {
placePerpOrder: jest.fn()
}
const { getDriftService } = require('../../../lib/drift/client')
getDriftService.mockReturnValue({
getClient: () => mockDriftClient
})
// Mock trading config
const { getMarketConfig } = require('../../../config/trading')
getMarketConfig.mockReturnValue({
driftMarketIndex: 0,
minOrderSize: 0.1
})
})
describe('Single Stop System', () => {
it('should return success when all 3 orders placed (TP1 + TP2 + SL)', async () => {
// Mock successful order placements
mockDriftClient.placePerpOrder
.mockResolvedValueOnce('TP1_SIG') // TP1
.mockResolvedValueOnce('TP2_SIG') // TP2
.mockResolvedValueOnce('SL_SIG') // SL
const options: PlaceExitOrdersOptions = {
symbol: 'SOL-PERP',
positionSizeUSD: 8000,
entryPrice: 140.00,
tp1Price: 141.20,
tp2Price: 142.41,
stopLossPrice: 138.71,
tp1SizePercent: 75,
tp2SizePercent: 0,
direction: 'long',
useDualStops: false
}
const result = await placeExitOrders(options)
console.log('RESULT success case', result)
expect(result.success).toBe(true)
expect(result.signatures).toHaveLength(3)
expect(result.signatures).toEqual(['TP1_SIG', 'TP2_SIG', 'SL_SIG'])
})
it('should return failure when SL placement fails', async () => {
// Mock TP1 and TP2 success, but SL fails
mockDriftClient.placePerpOrder
.mockResolvedValueOnce('TP1_SIG') // TP1
.mockResolvedValueOnce('TP2_SIG') // TP2
.mockRejectedValueOnce(new Error('Rate limit exceeded')) // SL fails
const options: PlaceExitOrdersOptions = {
symbol: 'SOL-PERP',
positionSizeUSD: 8000,
entryPrice: 140.00,
tp1Price: 141.20,
tp2Price: 142.41,
stopLossPrice: 138.71,
tp1SizePercent: 75,
tp2SizePercent: 0,
direction: 'long',
useDualStops: false
}
const result = await placeExitOrders(options)
expect(result.success).toBe(false)
expect(result.error).toContain('Stop loss placement failed')
})
})
describe('Dual Stop System', () => {
it('should return success when all 4 orders placed (TP1 + TP2 + Soft + Hard)', async () => {
// Mock successful order placements
mockDriftClient.placePerpOrder
.mockResolvedValueOnce('TP1_SIG') // TP1
.mockResolvedValueOnce('TP2_SIG') // TP2
.mockResolvedValueOnce('SOFT_SIG') // Soft Stop
.mockResolvedValueOnce('HARD_SIG') // Hard Stop
const options: PlaceExitOrdersOptions = {
symbol: 'SOL-PERP',
positionSizeUSD: 8000,
entryPrice: 140.00,
tp1Price: 141.20,
tp2Price: 142.41,
stopLossPrice: 138.71,
tp1SizePercent: 75,
tp2SizePercent: 0,
direction: 'long',
useDualStops: true,
softStopPrice: 139.00,
hardStopPrice: 138.50
}
const result = await placeExitOrders(options)
expect(result.success).toBe(true)
expect(result.signatures).toHaveLength(4)
expect(result.signatures).toEqual(['TP1_SIG', 'TP2_SIG', 'SOFT_SIG', 'HARD_SIG'])
})
it('should return failure when soft stop fails', async () => {
mockDriftClient.placePerpOrder
.mockResolvedValueOnce('TP1_SIG') // TP1
.mockResolvedValueOnce('TP2_SIG') // TP2
.mockRejectedValueOnce(new Error('Soft stop failed')) // Soft Stop
const options: PlaceExitOrdersOptions = {
symbol: 'SOL-PERP',
positionSizeUSD: 8000,
entryPrice: 140.00,
tp1Price: 141.20,
tp2Price: 142.41,
stopLossPrice: 138.71,
tp1SizePercent: 75,
tp2SizePercent: 0,
direction: 'long',
useDualStops: true,
softStopPrice: 139.00,
hardStopPrice: 138.50
}
const result = await placeExitOrders(options)
expect(result.success).toBe(false)
expect(result.error).toContain('Soft stop placement failed')
})
it('should return failure when hard stop fails', async () => {
mockDriftClient.placePerpOrder
.mockResolvedValueOnce('TP1_SIG') // TP1
.mockResolvedValueOnce('TP2_SIG') // TP2
.mockResolvedValueOnce('SOFT_SIG') // Soft Stop
.mockRejectedValueOnce(new Error('Hard stop failed')) // Hard Stop
const options: PlaceExitOrdersOptions = {
symbol: 'SOL-PERP',
positionSizeUSD: 8000,
entryPrice: 140.00,
tp1Price: 141.20,
tp2Price: 142.41,
stopLossPrice: 138.71,
tp1SizePercent: 75,
tp2SizePercent: 0,
direction: 'long',
useDualStops: true,
softStopPrice: 139.00,
hardStopPrice: 138.50
}
const result = await placeExitOrders(options)
expect(result.success).toBe(false)
expect(result.error).toContain('Hard stop placement failed')
})
})
})