/** * QuestDB Client Integration Test * * This test validates that all components work together correctly * without requiring an actual QuestDB instance. */ import { afterEach, beforeEach, describe, expect, it, mock, spyOn } from 'bun:test'; import { createQuestDBClient, QuestDBClient, QuestDBHealthMonitor, QuestDBInfluxWriter, QuestDBQueryBuilder, QuestDBSchemaManager, } from '../src'; import { questdbTestHelpers } from './setup'; describe('QuestDB Client Integration', () => { let client: QuestDBClient; beforeEach(() => { client = new QuestDBClient({ host: 'localhost', httpPort: 9000, pgPort: 8812, influxPort: 9009, database: 'questdb', user: 'admin', password: 'quest', }); }); afterEach(async () => { if (client && client.connected) { try { await client.disconnect(); } catch (error) { // Ignore cleanup errors in tests } } }); describe('Client Initialization', () => { it('should create client with factory function', () => { const factoryClient = createQuestDBClient(); expect(factoryClient).toBeInstanceOf(QuestDBClient); }); it('should initialize all supporting classes', () => { expect(client.getHealthMonitor()).toBeInstanceOf(QuestDBHealthMonitor); expect(client.queryBuilder()).toBeInstanceOf(QuestDBQueryBuilder); expect(client.getInfluxWriter()).toBeInstanceOf(QuestDBInfluxWriter); expect(client.getSchemaManager()).toBeInstanceOf(QuestDBSchemaManager); }); it('should handle connection configuration', () => { expect(client.getHttpUrl()).toBe('http://localhost:9000'); expect(client.getInfluxUrl()).toBe('http://localhost:9009'); expect(client.connected).toBe(false); }); }); describe('Query Builder', () => { it('should build query using query builder', () => { const query = client .queryBuilder() .select('symbol', 'close', 'timestamp') .from('ohlcv') .whereSymbol('AAPL') .whereLastHours(24) .orderBy('timestamp', 'DESC') .limit(100) .build(); expect(query).toContain('SELECT symbol, close, timestamp'); expect(query).toContain('FROM ohlcv'); expect(query).toContain("symbol = 'AAPL'"); expect(query).toContain('ORDER BY timestamp DESC'); expect(query).toContain('LIMIT 100'); expect(questdbTestHelpers.validateQuestDBQuery(query)).toBe(true); }); it('should build time-series specific queries', () => { const latestQuery = client .queryBuilder() .select('*') .from('ohlcv') .latestBy('symbol') .build(); expect(latestQuery).toContain('LATEST BY symbol'); expect(questdbTestHelpers.validateQuestDBQuery(latestQuery)).toBe(true); const sampleQuery = client .queryBuilder() .select('symbol', 'avg(close)') .from('ohlcv') .sampleBy('1d') .build(); expect(sampleQuery).toContain('SAMPLE BY 1d'); expect(questdbTestHelpers.validateQuestDBQuery(sampleQuery)).toBe(true); }); it('should build aggregation queries', () => { const query = client .aggregate('ohlcv') .select('symbol', 'avg(close) as avg_price', 'max(high) as max_high') .whereSymbolIn(['AAPL', 'GOOGL']) .groupBy('symbol') .sampleBy('1h') .build(); expect(query).toContain('SELECT symbol, avg(close) as avg_price, max(high) as max_high'); expect(query).toContain('FROM ohlcv'); expect(query).toContain("symbol IN ('AAPL', 'GOOGL')"); expect(query).toContain('SAMPLE BY 1h'); expect(query).toContain('GROUP BY symbol'); expect(questdbTestHelpers.validateQuestDBQuery(query)).toBe(true); }); }); describe('InfluxDB Writer', () => { it('should write OHLCV data using InfluxDB line protocol', async () => { const ohlcvData = [ { timestamp: new Date('2024-01-01T12:00:00Z'), open: 150.0, high: 152.0, low: 149.5, close: 151.5, volume: 1000000, }, ]; // Mock the actual write operation const writeSpy = spyOn(client.getInfluxWriter(), 'writeOHLCV'); writeSpy.mockReturnValue(Promise.resolve()); await expect(async () => { await client.writeOHLCV('AAPL', 'NASDAQ', ohlcvData); }).not.toThrow(); }); it('should handle batch operations', () => { const lines = questdbTestHelpers.generateInfluxDBLines(3); expect(lines.length).toBe(3); lines.forEach(line => { expect(line).toContain('ohlcv,symbol=TEST'); expect(line).toMatch(/\d{19}$/); // Nanosecond timestamp }); }); }); describe('Schema Manager', () => { it('should provide schema access', () => { const schema = client.getSchemaManager().getSchema('ohlcv_data'); expect(schema).toBeDefined(); expect(schema?.tableName).toBe('ohlcv_data'); const symbolColumn = schema?.columns.find(col => col.name === 'symbol'); expect(symbolColumn).toBeDefined(); expect(symbolColumn?.type).toBe('SYMBOL'); expect(schema?.partitionBy).toBe('DAY'); }); }); describe('Health Monitor', () => { it('should provide health monitoring capabilities', async () => { const healthMonitor = client.getHealthMonitor(); expect(healthMonitor).toBeInstanceOf(QuestDBHealthMonitor); // Mock health status since we're not connected const mockHealthStatus = { isHealthy: false, lastCheck: new Date(), responseTime: 100, message: 'Connection not established', details: { pgPool: false, httpEndpoint: false, uptime: 0, }, }; const healthSpy = spyOn(healthMonitor, 'getHealthStatus'); healthSpy.mockReturnValue(Promise.resolve(mockHealthStatus)); const health = await healthMonitor.getHealthStatus(); expect(health.isHealthy).toBe(false); expect(health.lastCheck).toBeInstanceOf(Date); expect(health.message).toBe('Connection not established'); }); }); describe('Time-Series Operations', () => { it('should support latest by operations', async () => { // Mock the query execution const mockResult = { rows: [{ symbol: 'AAPL', close: 150.0, timestamp: new Date() }], rowCount: 1, executionTime: 10, metadata: { columns: [] }, }; const querySpy = spyOn(client, 'query'); querySpy.mockReturnValue(Promise.resolve(mockResult)); const result = await client.latestBy('ohlcv', ['symbol', 'close'], 'symbol'); expect(result.rows.length).toBe(1); expect(result.rows[0].symbol).toBe('AAPL'); }); it('should support sample by operations', async () => { // Mock the query execution const mockResult = { rows: [{ symbol: 'AAPL', avg_close: 150.0, timestamp: new Date() }], rowCount: 1, executionTime: 15, metadata: { columns: [] }, }; const querySpy = spyOn(client, 'query'); querySpy.mockReturnValue(Promise.resolve(mockResult)); const result = await client.sampleBy( 'ohlcv', ['symbol', 'avg(close) as avg_close'], '1h', 'timestamp', "symbol = 'AAPL'" ); expect(result.rows.length).toBe(1); expect(result.executionTime).toBe(15); }); }); describe('Connection Management', () => { it('should handle connection configuration', () => { expect(client.getHttpUrl()).toBe('http://localhost:9000'); expect(client.getInfluxUrl()).toBe('http://localhost:9009'); expect(client.connected).toBe(false); }); it('should provide configuration access', () => { const config = client.configuration; expect(config.host).toBe('localhost'); expect(config.httpPort).toBe(9000); expect(config.user).toBe('admin'); }); }); });