import { describe, expect, it, vi } from 'vitest'; vi.mock('../lib/supabaseClient', () => { const query: any = { select: vi.fn(() => query), order: vi.fn(() => query), eq: vi.fn(() => query), limit: vi.fn(() => Promise.resolve({ data: [], error: null })) }; return { supabase: { from: vi.fn(() => query) } }; }); vi.mock('./AuthContext', () => ({ useAuth: () => ({ user: null, profile: { role: 'admin' } }) })); import { filterProfilesBySearch, normalizeSessionPresetValue, normalizeStrategyConfig, resolveSessionPresetSelection, summarizePortfolioStats } from './TradeProfileManager'; describe('normalizeStrategyConfig', () => { it('normalizes legacy session aliases into canonical preset values', () => { expect(normalizeSessionPresetValue('London,NY')).toBe('LDN,NY'); expect(normalizeSessionPresetValue('LDN | NY | TOK | SYD')).toBe('24/7'); expect(normalizeSessionPresetValue('24x7')).toBe('24/7'); expect(resolveSessionPresetSelection('Tokyo,Sydney')).toBe('TOK,SYD'); expect(resolveSessionPresetSelection('LDN,NY,custom')).toBe('LDN,NY'); expect(resolveSessionPresetSelection('LATAM')).toBe('custom'); }); it('returns safe defaults when config is missing', () => { const normalized = normalizeStrategyConfig(undefined as any); expect(normalized.rules).toEqual([]); expect(normalized.riskLimits.maxDailyLossUsd).toBe(50); expect(normalized.riskLimits.maxOpenTrades).toBe(3); expect(normalized.execution.orderType).toBe('market'); expect(normalized.execution.cooldownMinutes).toBe(30); expect(normalized.execution.entryMode).toBe('both'); }); it('normalizes legacy session and AI confidence parameters', () => { const normalized = normalizeStrategyConfig({ rules: [ { ruleId: 'SessionRule', enabled: true, params: { allowedSessions: 'London,NY' } }, { ruleId: 'AIAnalysisRule', enabled: true, params: { confidenceThreshold: 0.72 } } ], riskLimits: { maxDailyLossUsd: 100, maxOpenTrades: 4, maxConsecutiveLosses: 3 }, execution: { orderType: 'limit', cooldownMinutes: 12, entryMode: 'long_only' } } as any); const sessionRule = normalized.rules.find((rule) => rule.ruleId === 'SessionRule'); const aiRule = normalized.rules.find((rule) => rule.ruleId === 'AIAnalysisRule'); expect(sessionRule?.params?.sessions).toBe('London,NY'); expect(aiRule?.params?.minConfidence).toBe(72); expect(normalized.execution.orderType).toBe('limit'); expect(normalized.execution.entryMode).toBe('long_only'); }); it('coerces invalid numeric values back to defaults', () => { const normalized = normalizeStrategyConfig({ rules: [], riskLimits: { maxDailyLossUsd: Number.NaN, maxOpenTrades: 0, maxConsecutiveLosses: -1 }, execution: { orderType: 'unknown', cooldownMinutes: -5, longOnly: true } } as any); expect(normalized.riskLimits.maxDailyLossUsd).toBe(50); expect(normalized.riskLimits.maxOpenTrades).toBe(3); expect(normalized.riskLimits.maxConsecutiveLosses).toBe(2); expect(normalized.execution.orderType).toBe('market'); expect(normalized.execution.cooldownMinutes).toBe(30); expect(normalized.execution.entryMode).toBe('long_only'); }); it('preserves maxConsecutiveLosses=0 as disabled', () => { const normalized = normalizeStrategyConfig({ rules: [], riskLimits: { maxDailyLossUsd: 100, maxOpenTrades: 3, maxConsecutiveLosses: 0 }, execution: { orderType: 'market', cooldownMinutes: 10 } } as any); expect(normalized.riskLimits.maxConsecutiveLosses).toBe(0); }); it('filters profiles by search term case-insensitively', () => { const filtered = filterProfilesBySearch([ { id: '1', user_id: 'u1', name: 'High Risk Scalper', allocated_capital: 1000, risk_per_trade_percent: 1, symbols: 'BTC/USDT', is_active: true }, { id: '2', user_id: 'u1', name: 'Conservative Bag', allocated_capital: 2000, risk_per_trade_percent: 0.5, symbols: 'ETH/USDT', is_active: false } ] as any, 'conservative'); expect(filtered).toHaveLength(1); expect(filtered[0].name).toBe('Conservative Bag'); }); it('summarizes capital and pnl metrics', () => { const summary = summarizePortfolioStats( [ { id: '1', user_id: 'u1', name: 'A', allocated_capital: 1000, risk_per_trade_percent: 1, symbols: 'BTC/USDT', is_active: true }, { id: '2', user_id: 'u1', name: 'B', allocated_capital: 1500, risk_per_trade_percent: 1, symbols: 'ETH/USDT', is_active: false } ] as any, { '1': { winRate: 60, totalPnl: 120, tradeCount: 4 }, '2': { winRate: 50, totalPnl: -20, tradeCount: 2 } } ); expect(summary.activeCount).toBe(1); expect(summary.totalCapital).toBe(2500); expect(summary.totalPnl).toBe(100); expect(summary.totalTrades).toBe(6); }); });