stock-bot/libs/core/cache/test/key-generator.test.ts
2025-06-25 11:38:23 -04:00

220 lines
7.6 KiB
TypeScript

import { describe, expect, it } from 'bun:test';
import { CacheKeyGenerator, generateKey } from '../src/key-generator';
describe('CacheKeyGenerator', () => {
describe('marketData', () => {
it('should generate key with symbol, timeframe and date', () => {
const date = new Date('2024-01-15T10:30:00Z');
const key = CacheKeyGenerator.marketData('AAPL', '1h', date);
expect(key).toBe('market:aapl:1h:2024-01-15');
});
it('should generate key with "latest" when no date provided', () => {
const key = CacheKeyGenerator.marketData('MSFT', '1d');
expect(key).toBe('market:msft:1d:latest');
});
it('should lowercase the symbol', () => {
const key = CacheKeyGenerator.marketData('GOOGL', '5m');
expect(key).toBe('market:googl:5m:latest');
});
it('should handle different timeframes', () => {
expect(CacheKeyGenerator.marketData('TSLA', '1m')).toBe('market:tsla:1m:latest');
expect(CacheKeyGenerator.marketData('TSLA', '15m')).toBe('market:tsla:15m:latest');
expect(CacheKeyGenerator.marketData('TSLA', '1w')).toBe('market:tsla:1w:latest');
});
});
describe('indicator', () => {
it('should generate key with all parameters', () => {
const key = CacheKeyGenerator.indicator('AAPL', 'RSI', 14, 'abc123');
expect(key).toBe('indicator:aapl:RSI:14:abc123');
});
it('should lowercase the symbol but not indicator name', () => {
const key = CacheKeyGenerator.indicator('META', 'MACD', 20, 'hash456');
expect(key).toBe('indicator:meta:MACD:20:hash456');
});
it('should handle different period values', () => {
expect(CacheKeyGenerator.indicator('AMZN', 'SMA', 50, 'hash1')).toBe(
'indicator:amzn:SMA:50:hash1'
);
expect(CacheKeyGenerator.indicator('AMZN', 'SMA', 200, 'hash2')).toBe(
'indicator:amzn:SMA:200:hash2'
);
});
});
describe('backtest', () => {
it('should generate key with strategy name and hashed params', () => {
const params = { stopLoss: 0.02, takeProfit: 0.05 };
const key = CacheKeyGenerator.backtest('MomentumStrategy', params);
expect(key).toMatch(/^backtest:MomentumStrategy:[a-z0-9]+$/);
});
it('should generate same hash for same params regardless of order', () => {
const params1 = { a: 1, b: 2, c: 3 };
const params2 = { c: 3, a: 1, b: 2 };
const key1 = CacheKeyGenerator.backtest('Strategy', params1);
const key2 = CacheKeyGenerator.backtest('Strategy', params2);
expect(key1).toBe(key2);
});
it('should generate different hashes for different params', () => {
const params1 = { threshold: 0.01 };
const params2 = { threshold: 0.02 };
const key1 = CacheKeyGenerator.backtest('Strategy', params1);
const key2 = CacheKeyGenerator.backtest('Strategy', params2);
expect(key1).not.toBe(key2);
});
it('should handle complex nested params', () => {
const params = {
indicators: { rsi: { period: 14 }, macd: { fast: 12, slow: 26 } },
risk: { maxDrawdown: 0.1 },
};
const key = CacheKeyGenerator.backtest('ComplexStrategy', params);
expect(key).toMatch(/^backtest:ComplexStrategy:[a-z0-9]+$/);
});
});
describe('strategy', () => {
it('should generate key with strategy name, symbol and timeframe', () => {
const key = CacheKeyGenerator.strategy('TrendFollowing', 'NVDA', '4h');
expect(key).toBe('strategy:TrendFollowing:nvda:4h');
});
it('should lowercase the symbol but not strategy name', () => {
const key = CacheKeyGenerator.strategy('MeanReversion', 'AMD', '1d');
expect(key).toBe('strategy:MeanReversion:amd:1d');
});
});
describe('userSession', () => {
it('should generate key with userId', () => {
const key = CacheKeyGenerator.userSession('user123');
expect(key).toBe('session:user123');
});
it('should handle different userId formats', () => {
expect(CacheKeyGenerator.userSession('uuid-123-456')).toBe('session:uuid-123-456');
expect(CacheKeyGenerator.userSession('email@example.com')).toBe('session:email@example.com');
});
});
describe('portfolio', () => {
it('should generate key with userId and portfolioId', () => {
const key = CacheKeyGenerator.portfolio('user123', 'portfolio456');
expect(key).toBe('portfolio:user123:portfolio456');
});
it('should handle UUID format IDs', () => {
const key = CacheKeyGenerator.portfolio(
'550e8400-e29b-41d4-a716-446655440000',
'6ba7b810-9dad-11d1-80b4-00c04fd430c8'
);
expect(key).toBe(
'portfolio:550e8400-e29b-41d4-a716-446655440000:6ba7b810-9dad-11d1-80b4-00c04fd430c8'
);
});
});
describe('realtimePrice', () => {
it('should generate key with symbol', () => {
const key = CacheKeyGenerator.realtimePrice('BTC');
expect(key).toBe('price:realtime:btc');
});
it('should lowercase the symbol', () => {
const key = CacheKeyGenerator.realtimePrice('ETH-USD');
expect(key).toBe('price:realtime:eth-usd');
});
});
describe('orderBook', () => {
it('should generate key with symbol and default depth', () => {
const key = CacheKeyGenerator.orderBook('BTC');
expect(key).toBe('orderbook:btc:10');
});
it('should generate key with custom depth', () => {
const key = CacheKeyGenerator.orderBook('ETH', 20);
expect(key).toBe('orderbook:eth:20');
});
it('should lowercase the symbol', () => {
const key = CacheKeyGenerator.orderBook('USDT', 5);
expect(key).toBe('orderbook:usdt:5');
});
});
describe('hashObject', () => {
it('should generate consistent hashes', () => {
const params = { x: 1, y: 2 };
const key1 = CacheKeyGenerator.backtest('Test', params);
const key2 = CacheKeyGenerator.backtest('Test', params);
expect(key1).toBe(key2);
});
it('should handle empty objects', () => {
const key = CacheKeyGenerator.backtest('Empty', {});
expect(key).toMatch(/^backtest:Empty:[a-z0-9]+$/);
});
it('should handle arrays in objects', () => {
const params = { symbols: ['AAPL', 'MSFT'], periods: [10, 20, 30] };
const key = CacheKeyGenerator.backtest('ArrayTest', params);
expect(key).toMatch(/^backtest:ArrayTest:[a-z0-9]+$/);
});
it('should handle null and undefined values', () => {
const params = { a: null, b: undefined, c: 'value' };
const key = CacheKeyGenerator.backtest('NullTest', params);
expect(key).toMatch(/^backtest:NullTest:[a-z0-9]+$/);
});
});
});
describe('generateKey', () => {
it('should join parts with colons', () => {
const key = generateKey('user', 123, 'data');
expect(key).toBe('user:123:data');
});
it('should filter undefined values', () => {
const key = generateKey('prefix', undefined, 'suffix');
expect(key).toBe('prefix:suffix');
});
it('should convert all types to strings', () => {
const key = generateKey('bool', true, 'num', 42, 'str', 'text');
expect(key).toBe('bool:true:num:42:str:text');
});
it('should handle empty input', () => {
const key = generateKey();
expect(key).toBe('');
});
it('should handle single part', () => {
const key = generateKey('single');
expect(key).toBe('single');
});
it('should handle all undefined values', () => {
const key = generateKey(undefined, undefined, undefined);
expect(key).toBe('');
});
it('should handle boolean false', () => {
const key = generateKey('flag', false, 'end');
expect(key).toBe('flag:false:end');
});
it('should handle zero', () => {
const key = generateKey('count', 0, 'items');
expect(key).toBe('count:0:items');
});
});