"use strict"; /** * Contract test for POST /api/v1/hedging/sessions * Tests the API contract for creating hedging sessions */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const globals_1 = require("@jest/globals"); const axios_1 = __importDefault(require("axios")); (0, globals_1.describe)('POST /api/v1/hedging/sessions', () => { let client; const baseURL = 'http://localhost:3000/api/v1/hedging'; (0, globals_1.beforeAll)(() => { client = axios_1.default.create({ baseURL, headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer test-api-key' }, timeout: 5000 }); }); (0, globals_1.afterAll)(() => { // Cleanup if needed }); (0, globals_1.describe)('Request Schema Validation', () => { (0, globals_1.it)('should accept valid hedging session creation request', async () => { const validRequest = { name: 'Test Hedging Session', accountIds: ['account-1', 'account-2'], volumeTarget: 10000, strategy: { symbol: 'ETH/USD', volumeDistribution: 'equal', priceRange: { min: 0.001, max: 0.01 }, timing: { minInterval: 30, maxInterval: 120, orderSize: { min: 100, max: 500 } }, riskLimits: { maxPositionSize: 0.1, stopLossThreshold: 0.05, maxSlippage: 0.02 }, orderTypes: { primary: 'limit', fallback: 'market' } } }; try { const response = await client.post('/sessions', validRequest); // Should return 201 Created (0, globals_1.expect)(response.status).toBe(201); // Response should match schema const responseData = response.data; (0, globals_1.expect)(responseData.success).toBe(true); (0, globals_1.expect)(responseData.session).toBeDefined(); (0, globals_1.expect)(responseData.session.id).toBeDefined(); (0, globals_1.expect)(responseData.session.name).toBe(validRequest.name); (0, globals_1.expect)(responseData.session.status).toBe('pending'); (0, globals_1.expect)(responseData.session.accounts).toEqual(validRequest.accountIds); (0, globals_1.expect)(responseData.session.strategy).toEqual(validRequest.strategy); (0, globals_1.expect)(responseData.session.volumeTarget).toBe(validRequest.volumeTarget); (0, globals_1.expect)(responseData.session.volumeGenerated).toBe(0); (0, globals_1.expect)(responseData.session.startTime).toBeDefined(); (0, globals_1.expect)(responseData.session.riskBreaches).toEqual([]); (0, globals_1.expect)(responseData.session.orders).toEqual([]); } catch (error) { // This test should fail initially since the endpoint doesn't exist yet (0, globals_1.expect)(error.response?.status).toBe(404); } }); (0, globals_1.it)('should reject request with invalid session name', async () => { const invalidRequest = { name: 'ab', // Too short (less than 3 characters) accountIds: ['account-1', 'account-2'], volumeTarget: 10000, strategy: { symbol: 'ETH/USD', volumeDistribution: 'equal', priceRange: { min: 0.001, max: 0.01 }, timing: { minInterval: 30, maxInterval: 120, orderSize: { min: 100, max: 500 } }, riskLimits: { maxPositionSize: 0.1, stopLossThreshold: 0.05, maxSlippage: 0.02 }, orderTypes: { primary: 'limit', fallback: 'market' } } }; try { await client.post('/sessions', invalidRequest); fail('Should have rejected invalid request'); } catch (error) { (0, globals_1.expect)(error.response?.status).toBe(400); (0, globals_1.expect)(error.response?.data.success).toBe(false); (0, globals_1.expect)(error.response?.data.error.code).toBe('INVALID_STRATEGY'); } }); (0, globals_1.it)('should reject request with insufficient accounts', async () => { const invalidRequest = { name: 'Test Session', accountIds: ['account-1'], // Only one account (need at least 2) volumeTarget: 10000, strategy: { symbol: 'ETH/USD', volumeDistribution: 'equal', priceRange: { min: 0.001, max: 0.01 }, timing: { minInterval: 30, maxInterval: 120, orderSize: { min: 100, max: 500 } }, riskLimits: { maxPositionSize: 0.1, stopLossThreshold: 0.05, maxSlippage: 0.02 }, orderTypes: { primary: 'limit', fallback: 'market' } } }; try { await client.post('/sessions', invalidRequest); fail('Should have rejected invalid request'); } catch (error) { (0, globals_1.expect)(error.response?.status).toBe(400); (0, globals_1.expect)(error.response?.data.success).toBe(false); (0, globals_1.expect)(error.response?.data.error.code).toBe('INSUFFICIENT_ACCOUNTS'); } }); (0, globals_1.it)('should reject request with negative volume target', async () => { const invalidRequest = { name: 'Test Session', accountIds: ['account-1', 'account-2'], volumeTarget: -1000, // Negative volume target strategy: { symbol: 'ETH/USD', volumeDistribution: 'equal', priceRange: { min: 0.001, max: 0.01 }, timing: { minInterval: 30, maxInterval: 120, orderSize: { min: 100, max: 500 } }, riskLimits: { maxPositionSize: 0.1, stopLossThreshold: 0.05, maxSlippage: 0.02 }, orderTypes: { primary: 'limit', fallback: 'market' } } }; try { await client.post('/sessions', invalidRequest); fail('Should have rejected invalid request'); } catch (error) { (0, globals_1.expect)(error.response?.status).toBe(400); (0, globals_1.expect)(error.response?.data.success).toBe(false); (0, globals_1.expect)(error.response?.data.error.code).toBe('INVALID_STRATEGY'); } }); (0, globals_1.it)('should reject request with invalid strategy parameters', async () => { const invalidRequest = { name: 'Test Session', accountIds: ['account-1', 'account-2'], volumeTarget: 10000, strategy: { symbol: 'ETH/USD', volumeDistribution: 'equal', priceRange: { min: 0.01, max: 0.001 // min > max (invalid) }, timing: { minInterval: 30, maxInterval: 120, orderSize: { min: 100, max: 500 } }, riskLimits: { maxPositionSize: 0.1, stopLossThreshold: 0.05, maxSlippage: 0.02 }, orderTypes: { primary: 'limit', fallback: 'market' } } }; try { await client.post('/sessions', invalidRequest); fail('Should have rejected invalid request'); } catch (error) { (0, globals_1.expect)(error.response?.status).toBe(400); (0, globals_1.expect)(error.response?.data.success).toBe(false); (0, globals_1.expect)(error.response?.data.error.code).toBe('INVALID_STRATEGY'); } }); }); (0, globals_1.describe)('Authentication', () => { (0, globals_1.it)('should reject request without authorization header', async () => { const clientWithoutAuth = axios_1.default.create({ baseURL, headers: { 'Content-Type': 'application/json' } }); const validRequest = { name: 'Test Session', accountIds: ['account-1', 'account-2'], volumeTarget: 10000, strategy: { symbol: 'ETH/USD', volumeDistribution: 'equal', priceRange: { min: 0.001, max: 0.01 }, timing: { minInterval: 30, maxInterval: 120, orderSize: { min: 100, max: 500 } }, riskLimits: { maxPositionSize: 0.1, stopLossThreshold: 0.05, maxSlippage: 0.02 }, orderTypes: { primary: 'limit', fallback: 'market' } } }; try { await clientWithoutAuth.post('/sessions', validRequest); fail('Should have rejected unauthorized request'); } catch (error) { (0, globals_1.expect)(error.response?.status).toBe(401); } }); (0, globals_1.it)('should reject request with invalid authorization token', async () => { const clientWithInvalidAuth = axios_1.default.create({ baseURL, headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer invalid-token' } }); const validRequest = { name: 'Test Session', accountIds: ['account-1', 'account-2'], volumeTarget: 10000, strategy: { symbol: 'ETH/USD', volumeDistribution: 'equal', priceRange: { min: 0.001, max: 0.01 }, timing: { minInterval: 30, maxInterval: 120, orderSize: { min: 100, max: 500 } }, riskLimits: { maxPositionSize: 0.1, stopLossThreshold: 0.05, maxSlippage: 0.02 }, orderTypes: { primary: 'limit', fallback: 'market' } } }; try { await clientWithInvalidAuth.post('/sessions', validRequest); fail('Should have rejected request with invalid token'); } catch (error) { (0, globals_1.expect)(error.response?.status).toBe(401); } }); }); (0, globals_1.describe)('Business Logic Validation', () => { (0, globals_1.it)('should reject request with inactive accounts', async () => { const requestWithInactiveAccount = { name: 'Test Session', accountIds: ['account-1', 'inactive-account'], // inactive-account is not active volumeTarget: 10000, strategy: { symbol: 'ETH/USD', volumeDistribution: 'equal', priceRange: { min: 0.001, max: 0.01 }, timing: { minInterval: 30, maxInterval: 120, orderSize: { min: 100, max: 500 } }, riskLimits: { maxPositionSize: 0.1, stopLossThreshold: 0.05, maxSlippage: 0.02 }, orderTypes: { primary: 'limit', fallback: 'market' } } }; try { await client.post('/sessions', requestWithInactiveAccount); fail('Should have rejected request with inactive account'); } catch (error) { (0, globals_1.expect)(error.response?.status).toBe(400); (0, globals_1.expect)(error.response?.data.success).toBe(false); (0, globals_1.expect)(error.response?.data.error.code).toBe('ACCOUNT_NOT_ACTIVE'); } }); (0, globals_1.it)('should reject request with insufficient account balance', async () => { const requestWithInsufficientBalance = { name: 'Test Session', accountIds: ['account-1', 'poor-account'], // poor-account has insufficient balance volumeTarget: 1000000, // Very high volume target strategy: { symbol: 'ETH/USD', volumeDistribution: 'equal', priceRange: { min: 0.001, max: 0.01 }, timing: { minInterval: 30, maxInterval: 120, orderSize: { min: 100, max: 500 } }, riskLimits: { maxPositionSize: 0.1, stopLossThreshold: 0.05, maxSlippage: 0.02 }, orderTypes: { primary: 'limit', fallback: 'market' } } }; try { await client.post('/sessions', requestWithInsufficientBalance); fail('Should have rejected request with insufficient balance'); } catch (error) { (0, globals_1.expect)(error.response?.status).toBe(400); (0, globals_1.expect)(error.response?.data.success).toBe(false); (0, globals_1.expect)(error.response?.data.error.code).toBe('INSUFFICIENT_BALANCE'); } }); }); }); //# sourceMappingURL=test_hedging_sessions_post.js.map