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