goldenChat base source add

This commit is contained in:
aidev
2026-05-23 15:11:48 +09:00
commit a4ea7762b5
2081 changed files with 1155760 additions and 0 deletions
@@ -0,0 +1,321 @@
/**
* 실시간 업데이트 통합 테스트
*/
import { renderHook, act, waitFor } from '@testing-library/react';
import { useRealtimeCandles } from '../hooks/useRealtimeCandles';
import { marketSocket } from '../services/marketSocket';
// Mock marketSocket
jest.mock('../services/marketSocket', () => ({
marketSocket: {
connect: jest.fn().mockResolvedValue(undefined),
subscribe: jest.fn().mockReturnValue(() => {}),
unsubscribe: jest.fn(),
onConnectionStatusChange: jest.fn().mockReturnValue(() => {}),
onError: jest.fn().mockReturnValue(() => {}),
},
}));
describe('useRealtimeCandles Hook', () => {
beforeEach(() => {
jest.clearAllMocks();
});
it('should connect and subscribe when enabled', async () => {
const { result } = renderHook(() =>
useRealtimeCandles({
market: 'KRW-BTC',
interval: '1m',
enabled: true,
})
);
await waitFor(() => {
expect(marketSocket.connect).toHaveBeenCalled();
expect(marketSocket.subscribe).toHaveBeenCalledWith(
'KRW-BTC',
'1m',
expect.any(Function)
);
});
});
it('should not connect when disabled', () => {
renderHook(() =>
useRealtimeCandles({
market: 'KRW-BTC',
interval: '1m',
enabled: false,
})
);
expect(marketSocket.connect).not.toHaveBeenCalled();
expect(marketSocket.subscribe).not.toHaveBeenCalled();
});
it('should update candles on snapshot', async () => {
const onUpdate = jest.fn();
const { result } = renderHook(() =>
useRealtimeCandles({
market: 'KRW-BTC',
interval: '1m',
enabled: true,
onUpdate,
})
);
// Simulate snapshot update
const mockUpdate = {
market: 'KRW-BTC',
interval: '1m',
candles: [
{ time: '2026-03-07T01:00', open: 100, high: 110, low: 90, close: 105, volume: 1000 },
{ time: '2026-03-07T01:01', open: 105, high: 115, low: 95, close: 110, volume: 1200 },
],
timestamp: Date.now(),
type: 'snapshot' as const,
};
// Get the callback that was passed to subscribe
const subscribeCall = (marketSocket.subscribe as jest.Mock).mock.calls[0];
const callback = subscribeCall[2];
act(() => {
callback(mockUpdate);
});
await waitFor(() => {
expect(result.current.candles).toHaveLength(2);
expect(result.current.updateCount).toBe(1);
expect(onUpdate).toHaveBeenCalledWith(mockUpdate.candles);
});
});
it('should update existing candle on incremental update', async () => {
const onUpdate = jest.fn();
const { result } = renderHook(() =>
useRealtimeCandles({
market: 'KRW-BTC',
interval: '1m',
enabled: true,
onUpdate,
})
);
// Initial snapshot
const snapshotUpdate = {
market: 'KRW-BTC',
interval: '1m',
candles: [
{ time: '2026-03-07T01:00', open: 100, high: 110, low: 90, close: 105, volume: 1000 },
],
timestamp: Date.now(),
type: 'snapshot' as const,
};
const subscribeCall = (marketSocket.subscribe as jest.Mock).mock.calls[0];
const callback = subscribeCall[2];
act(() => {
callback(snapshotUpdate);
});
// Incremental update (same time, different values)
const incrementalUpdate = {
market: 'KRW-BTC',
interval: '1m',
candles: [
{ time: '2026-03-07T01:00', open: 100, high: 115, low: 85, close: 108, volume: 1500 },
],
timestamp: Date.now(),
type: 'update' as const,
};
act(() => {
callback(incrementalUpdate);
});
await waitFor(() => {
expect(result.current.candles).toHaveLength(1);
expect(result.current.candles[0].close).toBe(108);
expect(result.current.candles[0].volume).toBe(1500);
expect(result.current.updateCount).toBe(2);
});
});
it('should add new candle on incremental update', async () => {
const onUpdate = jest.fn();
const { result } = renderHook(() =>
useRealtimeCandles({
market: 'KRW-BTC',
interval: '1m',
enabled: true,
onUpdate,
})
);
// Initial snapshot
const snapshotUpdate = {
market: 'KRW-BTC',
interval: '1m',
candles: [
{ time: '2026-03-07T01:00', open: 100, high: 110, low: 90, close: 105, volume: 1000 },
],
timestamp: Date.now(),
type: 'snapshot' as const,
};
const subscribeCall = (marketSocket.subscribe as jest.Mock).mock.calls[0];
const callback = subscribeCall[2];
act(() => {
callback(snapshotUpdate);
});
// New candle
const newCandleUpdate = {
market: 'KRW-BTC',
interval: '1m',
candles: [
{ time: '2026-03-07T01:01', open: 105, high: 115, low: 95, close: 110, volume: 1200 },
],
timestamp: Date.now(),
type: 'update' as const,
};
act(() => {
callback(newCandleUpdate);
});
await waitFor(() => {
expect(result.current.candles).toHaveLength(2);
expect(result.current.candles[1].time).toBe('2026-03-07T01:01');
expect(result.current.updateCount).toBe(2);
});
});
it('should cleanup on unmount', () => {
const { unmount } = renderHook(() =>
useRealtimeCandles({
market: 'KRW-BTC',
interval: '1m',
enabled: true,
})
);
const unsubscribeFn = jest.fn();
(marketSocket.subscribe as jest.Mock).mockReturnValue(unsubscribeFn);
unmount();
// Note: In actual implementation, unsubscribe is called in cleanup
// This test verifies the hook structure
});
});
describe('Real-time Chart Integration', () => {
it('should handle timeframe switching', async () => {
const { result, rerender } = renderHook(
({ market, interval }) =>
useRealtimeCandles({
market,
interval,
enabled: true,
}),
{
initialProps: { market: 'KRW-BTC', interval: '1m' },
}
);
// Initial subscription
expect(marketSocket.subscribe).toHaveBeenCalledWith(
'KRW-BTC',
'1m',
expect.any(Function)
);
// Change interval
rerender({ market: 'KRW-BTC', interval: '5m' });
await waitFor(() => {
// Should resubscribe with new interval
expect(marketSocket.subscribe).toHaveBeenCalledWith(
'KRW-BTC',
'5m',
expect.any(Function)
);
});
});
it('should handle market switching', async () => {
const { result, rerender } = renderHook(
({ market, interval }) =>
useRealtimeCandles({
market,
interval,
enabled: true,
}),
{
initialProps: { market: 'KRW-BTC', interval: '1m' },
}
);
// Change market
rerender({ market: 'KRW-ETH', interval: '1m' });
await waitFor(() => {
// Should resubscribe with new market
expect(marketSocket.subscribe).toHaveBeenCalledWith(
'KRW-ETH',
'1m',
expect.any(Function)
);
});
});
});
describe('Indicator Recalculation', () => {
it('should trigger indicator recalculation on candle update', async () => {
// This test would require mocking the full CandlestickTestPage component
// For now, we verify the hook behavior
const onUpdate = jest.fn();
const { result } = renderHook(() =>
useRealtimeCandles({
market: 'KRW-BTC',
interval: '1m',
enabled: true,
onUpdate,
})
);
const mockUpdate = {
market: 'KRW-BTC',
interval: '1m',
candles: [
{ time: '2026-03-07T01:00', open: 100, high: 110, low: 90, close: 105, volume: 1000 },
],
timestamp: Date.now(),
type: 'update' as const,
};
const subscribeCall = (marketSocket.subscribe as jest.Mock).mock.calls[0];
const callback = subscribeCall[2];
act(() => {
callback(mockUpdate);
});
await waitFor(() => {
expect(onUpdate).toHaveBeenCalled();
expect(onUpdate).toHaveBeenCalledWith(
expect.arrayContaining([
expect.objectContaining({
time: '2026-03-07T01:00',
close: 105,
}),
])
);
});
});
});