learning_ai_invt_trdg/web/src/components/TradeProfileManager.test.ts

154 lines
5.7 KiB
TypeScript

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);
});
});