/** * 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') }) }) })