added questdb-client integration tests

This commit is contained in:
Bojan Kucera 2025-06-04 14:15:36 -04:00
parent d0bc9cf32f
commit 674112af05
3 changed files with 379 additions and 38 deletions

View file

@ -5,7 +5,7 @@
* without requiring an actual QuestDB instance.
*/
import { describe, it, expect, beforeEach, afterEach } from 'bun:test';
import { describe, it, expect, beforeEach, afterEach, mock, spyOn } from 'bun:test';
import {
QuestDBClient,
QuestDBHealthMonitor,
@ -17,8 +17,7 @@ import {
import { questdbTestHelpers } from './setup';
describe('QuestDB Client Integration', () => {
let client: QuestDBClient;
beforeEach(() => {
let client: QuestDBClient; beforeEach(() => {
client = new QuestDBClient({
host: 'localhost',
httpPort: 9000,
@ -28,14 +27,13 @@ describe('QuestDB Client Integration', () => {
user: 'admin',
password: 'quest'
});
// Add connected property for tests
(client as any).connected = false;
});
afterEach(async () => {
if (client.connected) {
await client.disconnect();
}); afterEach(async () => {
if (client && client.connected) {
try {
await client.disconnect();
} catch (error) {
// Ignore cleanup errors in tests
}
}
});
@ -114,7 +112,6 @@ describe('QuestDB Client Integration', () => {
expect(questdbTestHelpers.validateQuestDBQuery(query)).toBe(true);
});
});
describe('InfluxDB Writer', () => {
it('should write OHLCV data using InfluxDB line protocol', async () => {
const ohlcvData = [{
@ -124,22 +121,26 @@ describe('QuestDB Client Integration', () => {
low: 149.50,
close: 151.50,
volume: 1000000
}]; // Mock the actual write operation
jest.spyOn(client.getInfluxWriter(), 'writeOHLCV').mockResolvedValue();
}];
await expect(async () => {
// 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', () => {
});
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', () => {
}); });
});
describe('Schema Manager', () => {
it('should provide schema access', () => {
const schema = client.getSchemaManager().getSchema('ohlcv_data');
@ -150,13 +151,15 @@ describe('QuestDB Client Integration', () => {
expect(symbolColumn).toBeDefined();
expect(symbolColumn?.type).toBe('SYMBOL');
expect(schema?.partitionBy).toBe('DAY');
});
}); describe('Health Monitor', () => {
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
// Mock health status since we're not connected
const mockHealthStatus = {
isHealthy: false,
lastCheck: new Date(),
@ -165,11 +168,11 @@ describe('QuestDB Client Integration', () => {
details: {
pgPool: false,
httpEndpoint: false,
uptime: 0
}
uptime: 0 }
};
jest.spyOn(healthMonitor, 'getHealthStatus').mockResolvedValue(mockHealthStatus);
const healthSpy = spyOn(healthMonitor, 'getHealthStatus');
healthSpy.mockReturnValue(Promise.resolve(mockHealthStatus));
const health = await healthMonitor.getHealthStatus();
expect(health.isHealthy).toBe(false);
@ -177,7 +180,6 @@ describe('QuestDB Client Integration', () => {
expect(health.message).toBe('Connection not established');
});
});
describe('Time-Series Operations', () => {
it('should support latest by operations', async () => {
// Mock the query execution
@ -186,10 +188,12 @@ describe('QuestDB Client Integration', () => {
rowCount: 1,
executionTime: 10,
metadata: { columns: [] }
}; jest.spyOn(client, 'query').mockResolvedValue(mockResult);
};
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);
const result = await client.latestBy('ohlcv', ['symbol', 'close'], 'symbol'); expect(result.rows.length).toBe(1);
expect(result.rows[0].symbol).toBe('AAPL');
});
@ -204,13 +208,13 @@ describe('QuestDB Client Integration', () => {
metadata: { columns: [] }
};
jest.spyOn(client, 'query').mockResolvedValue(mockResult);
const result = await client.sampleBy(
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'"
'timestamp',
"symbol = 'AAPL'"
);
expect(result.rows.length).toBe(1);

View file

@ -6,6 +6,7 @@
*/
import { newDb } from 'pg-mem';
import { mock, spyOn, beforeAll, beforeEach } from 'bun:test';
// Mock PostgreSQL database for unit tests
let pgMem: any;
@ -43,10 +44,73 @@ beforeAll(() => {
throw new Error(`Unsupported date unit: ${unit}`);
}
return result;
}
}); // Mock QuestDB HTTP client
(global as any).fetch = () => {}; // Using Bun's built-in spyOn utilities
global.spyOn(global, 'fetch');
} }); // Mock QuestDB HTTP client
// Mock fetch using Bun's built-in mock
(global as any).fetch = mock(() => {});
// Mock the logger module to avoid Pino configuration conflicts
mock.module('@stock-bot/logger', () => ({
Logger: mock(() => ({
info: mock(() => {}),
warn: mock(() => {}),
error: mock(() => {}),
debug: mock(() => {}),
fatal: mock(() => {}),
trace: mock(() => {}),
child: mock(() => ({
info: mock(() => {}),
warn: mock(() => {}),
error: mock(() => {}),
debug: mock(() => {}),
fatal: mock(() => {}),
trace: mock(() => {}),
}))
})),
getLogger: mock(() => ({
info: mock(() => {}),
warn: mock(() => {}),
error: mock(() => {}),
debug: mock(() => {}),
fatal: mock(() => {}),
trace: mock(() => {}),
child: mock(() => ({
info: mock(() => {}),
warn: mock(() => {}),
error: mock(() => {}),
debug: mock(() => {}),
fatal: mock(() => {}),
trace: mock(() => {}),
}))
}))
}));
// Mock Pino and its transports to avoid configuration conflicts
mock.module('pino', () => ({
default: mock(() => ({
info: mock(() => {}),
warn: mock(() => {}),
error: mock(() => {}),
debug: mock(() => {}),
fatal: mock(() => {}),
trace: mock(() => {}),
child: mock(() => ({
info: mock(() => {}),
warn: mock(() => {}),
error: mock(() => {}),
debug: mock(() => {}),
fatal: mock(() => {}),
trace: mock(() => {}),
}))
}))
}));
mock.module('pino-pretty', () => ({
default: mock(() => ({}))
}));
mock.module('pino-loki', () => ({
default: mock(() => ({}))
}));
});
beforeEach(() => {