220 lines
7.6 KiB
TypeScript
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');
|
|
});
|
|
});
|