- Removed incorrect exclusion of *.test.ts and *.test.js files - Added coverage/ folder to .gitignore - Removed accidentally committed coverage files Co-authored-by: mindesbunister <32161838+mindesbunister@users.noreply.github.com>
Position Manager Tests
Overview
Comprehensive integration test suite for the Position Manager (lib/trading/position-manager.ts), which manages ~1,938 lines of critical trading logic handling real capital.
Quick Start
# Run all tests
npm test
# Run tests in watch mode (development)
npm run test:watch
# Run tests with coverage report
npm run test:coverage
# Run tests for CI (with JUnit reporter)
npm run test:ci
Test Structure
tests/
├── setup.ts # Global test configuration and mocks
├── helpers/
│ └── trade-factory.ts # Factory functions for creating mock trades
├── integration/
│ └── position-manager/
│ ├── tp1-detection.test.ts # Take Profit 1 detection tests
│ ├── breakeven-sl.test.ts # Breakeven SL after TP1 tests
│ ├── adx-runner-sl.test.ts # ADX-based runner SL tests
│ ├── trailing-stop.test.ts # Trailing stop functionality tests
│ ├── edge-cases.test.ts # Edge cases and common pitfalls
│ ├── price-verification.test.ts # Price verification before TP flags
│ └── decision-helpers.test.ts # Decision helper function tests
└── README.md # This file
Test Coverage
These tests verify the logic of Position Manager functions in isolation:
- Decision helper functions (shouldStopLoss, shouldTakeProfit1, etc.)
- Price calculation functions (calculatePrice, calculateProfitPercent)
- ADX-based SL positioning logic
- Trailing stop mechanics
- Token vs USD conversion requirements
- Price verification requirements
The tests extract and validate the pure calculation logic without importing the actual Position Manager, avoiding complex mocking of:
- Drift blockchain connections
- Pyth price feeds
- Database operations
- WebSocket subscriptions
This approach:
- Validates critical trading logic is correct
- Prevents regression of known bugs (Common Pitfalls)
- Enables safe refactoring of calculation functions
- Runs quickly without external dependencies
Test Data Standards
All tests use standardized test data based on actual trading conditions:
TEST_DEFAULTS = {
entry: 140.00, // Entry price
atr: 0.43, // ATR value
adx: 26.9, // ADX (strong trend)
qualityScore: 95, // Signal quality
positionSize: 8000, // Position size USD
leverage: 15, // Leverage multiplier
// LONG targets
long: {
tp1: 141.20, // +0.86%
tp2: 142.41, // +1.72%
sl: 138.71, // -0.92%
emergencySl: 137.20, // -2%
},
// SHORT targets
short: {
tp1: 138.80, // -0.86%
tp2: 137.59, // -1.72%
sl: 141.29, // +0.92%
emergencySl: 142.80, // +2%
},
}
Common Pitfalls Covered
These tests specifically prevent known bugs documented in the codebase:
| Pitfall # | Issue | Test File |
|---|---|---|
| #24 | Position.size as tokens, not USD | edge-cases.test.ts |
| #43 | TP1 false detection without price check | price-verification.test.ts |
| #45 | Wrong entry price for breakeven SL | breakeven-sl.test.ts |
| #52 | ADX-based runner SL positioning | adx-runner-sl.test.ts |
| #54 | MAE/MFE as percentages, not dollars | edge-cases.test.ts |
| #67 | Duplicate closures (atomic deduplication) | Covered by mock structure |
Test Helpers
Trade Factory (tests/helpers/trade-factory.ts)
import { createLongTrade, createShortTrade, createTradeAfterTP1, createTradeAfterTP2 } from '../helpers/trade-factory'
// Create a basic LONG trade
const longTrade = createLongTrade()
// Create a SHORT trade with custom entry
const shortTrade = createShortTrade({ entryPrice: 150 })
// Create a trade after TP1 hit (40% runner remaining)
const runnerTrade = createTradeAfterTP1('long')
// Create a trade with trailing stop active
const trailingTrade = createTradeAfterTP2('short')
Custom Matchers
// Check if value is within a range
expect(0.86).toBeWithinRange(0.8, 0.9) // passes
Why These Tests Matter
-
Financial Protection: Position Manager handles real money ($540+ capital). Bugs cost real dollars.
-
Regression Prevention: 71+ documented bugs in the codebase. Tests prevent reintroduction.
-
Safe Refactoring: With test coverage, code can be improved without fear of breaking existing functionality.
-
Documentation: Tests serve as executable documentation of expected behavior.
-
CI/CD Pipeline: Automated testing ensures changes don't break critical trading logic.
Writing New Tests
Guidelines
- Use Factories: Always use
createLongTrade()orcreateShortTrade()instead of manual object creation - Test Both Directions: Every price-based test should cover both LONG and SHORT positions
- Test Edge Cases: Include boundary conditions and error scenarios
- Clear Names: Test names should describe the exact behavior being tested
- Reference Pitfalls: When testing a known bug, reference the pitfall number in comments
Example Test
import { createLongTrade, createShortTrade, TEST_DEFAULTS } from '../../helpers/trade-factory'
describe('New Feature', () => {
it('should handle LONG position correctly', () => {
const trade = createLongTrade()
// ... test logic
expect(result).toBe(expected)
})
it('should handle SHORT position correctly', () => {
const trade = createShortTrade()
// ... test logic
expect(result).toBe(expected)
})
// Reference known bugs
it('should NOT trigger false positive (Pitfall #XX)', () => {
// ... regression test
})
})
Mocked Dependencies
The test setup mocks external dependencies to isolate tests:
- Drift Service: No actual blockchain calls
- Pyth Price Monitor: No WebSocket connections
- Database Operations: No actual DB queries
- Telegram Notifications: No actual messages sent
- Drift Orders: No actual order placement
Running Specific Tests
# Run a specific test file
npm test -- tests/integration/position-manager/tp1-detection.test.ts
# Run tests matching a pattern
npm test -- --testNamePattern="LONG"
# Run tests in a specific directory
npm test -- tests/integration/position-manager/
CI Integration
Tests run automatically in CI with:
- JUnit XML reports for test results
- Coverage reports in HTML and text formats
- Failure threshold of 60% coverage
Configure in jest.config.js:
coverageThreshold: {
global: {
branches: 60,
functions: 60,
lines: 60,
statements: 60,
},
},