Co-authored-by: mindesbunister <32161838+mindesbunister@users.noreply.github.com>
This commit is contained in:
183
tests/integration/orders/exit-orders-validation.test.ts
Normal file
183
tests/integration/orders/exit-orders-validation.test.ts
Normal file
@@ -0,0 +1,183 @@
|
||||
/**
|
||||
* 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)
|
||||
|
||||
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')
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user