@hypequery/clickhouse 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/dist/cli/bin.js +128 -36
  2. package/dist/cli/generate-types.js +101 -12
  3. package/dist/core/connection.d.ts +145 -0
  4. package/dist/core/connection.d.ts.map +1 -1
  5. package/dist/core/connection.js +75 -0
  6. package/dist/core/cross-filter.d.ts +85 -0
  7. package/dist/core/features/aggregations.d.ts +102 -0
  8. package/dist/core/features/analytics.d.ts +66 -0
  9. package/dist/core/features/executor.d.ts +19 -0
  10. package/dist/core/features/filtering.d.ts +29 -0
  11. package/dist/core/features/joins.d.ts +29 -0
  12. package/dist/core/features/pagination.d.ts +23 -0
  13. package/dist/core/features/query-modifiers.d.ts +119 -0
  14. package/dist/core/formatters/sql-formatter.d.ts +9 -0
  15. package/dist/core/join-relationships.d.ts +50 -0
  16. package/dist/core/query-builder.d.ts +197 -0
  17. package/dist/core/tests/index.d.ts +2 -0
  18. package/dist/core/tests/integration/setup.d.ts +40 -0
  19. package/dist/core/tests/integration/setup.d.ts.map +1 -1
  20. package/dist/core/tests/integration/setup.js +279 -237
  21. package/dist/core/tests/integration/test-initializer.d.ts +7 -0
  22. package/dist/core/tests/integration/test-initializer.d.ts.map +1 -0
  23. package/dist/core/tests/integration/test-initializer.js +32 -0
  24. package/dist/core/tests/test-utils.d.ts +30 -0
  25. package/dist/core/utils/logger.d.ts +37 -0
  26. package/dist/core/utils/sql-expressions.d.ts +59 -0
  27. package/dist/core/utils.d.ts +3 -0
  28. package/dist/core/validators/filter-validator.d.ts +8 -0
  29. package/dist/core/validators/value-validator.d.ts +6 -0
  30. package/dist/formatters/index.d.ts +1 -0
  31. package/dist/index.d.ts +10 -27
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +14 -2
  34. package/dist/types/base.d.ts +76 -0
  35. package/dist/types/clickhouse-types.d.ts +13 -0
  36. package/dist/types/filters.d.ts +37 -0
  37. package/dist/types/index.d.ts +3 -0
  38. package/package.json +15 -8
@@ -1,274 +1,316 @@
1
- import { execSync } from 'child_process';
2
- import { createQueryBuilder } from '../../../index';
1
+ import path from 'path';
2
+ import { fileURLToPath } from 'url';
3
3
  import { ClickHouseConnection } from '../../connection';
4
- // Configuration for the test ClickHouse instance
5
- const CLICKHOUSE_HOST = process.env.CLICKHOUSE_TEST_HOST || 'http://localhost:8123';
6
- const CLICKHOUSE_USER = process.env.CLICKHOUSE_TEST_USER || 'hypequery';
7
- const CLICKHOUSE_PASSWORD = process.env.CLICKHOUSE_TEST_PASSWORD || 'hypequery_test';
8
- const CLICKHOUSE_DB = process.env.CLICKHOUSE_TEST_DB || 'test_db';
9
- // Log connection details for debugging
10
- console.log('Initializing ClickHouse connection with:', {
11
- host: CLICKHOUSE_HOST,
12
- user: CLICKHOUSE_USER,
13
- password: CLICKHOUSE_PASSWORD ? 'PROVIDED' : 'NOT_PROVIDED',
14
- database: CLICKHOUSE_DB
15
- });
16
- // Helper to initialize the connection
17
- export async function initializeTestConnection() {
4
+ import { exec } from 'child_process';
5
+ import { promisify } from 'util';
6
+ import { logger as hypeQueryLogger } from '../../utils/logger';
7
+ // Disable the HypeQuery logger to prevent "logs after tests" errors
8
+ // This must be done early in the setup, before any queries run
9
+ hypeQueryLogger.configure({ enabled: false });
10
+ // Setup a logger that respects test environment
11
+ const logger = {
12
+ info: (message, ...args) => {
13
+ if (process.env.DEBUG === 'true') {
14
+ console.log(`[INFO] ${message}`, ...args);
15
+ }
16
+ },
17
+ error: (message, ...args) => {
18
+ if (process.env.DEBUG === 'true' || process.env.SUPPRESS_ERRORS !== 'true') {
19
+ console.error(`[ERROR] ${message}`, ...args);
20
+ }
21
+ },
22
+ warn: (message, ...args) => {
23
+ if (process.env.DEBUG === 'true') {
24
+ console.warn(`[WARN] ${message}`, ...args);
25
+ }
26
+ }
27
+ };
28
+ const execAsync = promisify(exec);
29
+ // Create a path to the project root
30
+ const __filename = fileURLToPath(import.meta.url);
31
+ const __dirname = path.dirname(__filename);
32
+ const projectRoot = path.resolve(__dirname, '../../../../../');
33
+ // Connection configuration (with defaults that can be overridden by env variables)
34
+ const config = {
35
+ host: process.env.CLICKHOUSE_TEST_HOST || 'http://localhost:8123',
36
+ user: process.env.CLICKHOUSE_TEST_USER || 'default',
37
+ password: process.env.CLICKHOUSE_TEST_PASSWORD || 'hypequery_test',
38
+ database: process.env.CLICKHOUSE_TEST_DB || 'test_db',
39
+ };
40
+ // Initialize the ClickHouse connection
41
+ export const initializeTestConnection = async () => {
42
+ logger.info('Initializing ClickHouse connection with config:', config);
18
43
  try {
19
- ClickHouseConnection.initialize({
20
- host: CLICKHOUSE_HOST,
21
- username: CLICKHOUSE_USER,
22
- password: CLICKHOUSE_PASSWORD,
23
- database: CLICKHOUSE_DB
24
- });
25
- // Test the connection
44
+ // Make sure ClickHouse is initialized
45
+ ensureConnectionInitialized();
46
+ // Test connection by getting client and pinging
26
47
  const client = ClickHouseConnection.getClient();
27
48
  await client.ping();
28
- console.log('ClickHouse connection successfully established');
49
+ logger.info('ClickHouse connection successful');
50
+ // Return the query builder from the index file
51
+ const { createQueryBuilder } = await import('../../../index.js');
29
52
  return createQueryBuilder({
30
- host: CLICKHOUSE_HOST,
31
- username: CLICKHOUSE_USER,
32
- password: CLICKHOUSE_PASSWORD,
33
- database: CLICKHOUSE_DB
53
+ host: config.host,
54
+ username: config.user,
55
+ password: config.password,
56
+ database: config.database,
34
57
  });
35
58
  }
36
59
  catch (error) {
37
- console.error('Failed to initialize ClickHouse connection:', error);
60
+ logger.error('Failed to connect to ClickHouse:', error);
38
61
  throw error;
39
62
  }
40
- }
41
- // SQL to create test tables
42
- const CREATE_TEST_TABLE = `
43
- CREATE TABLE IF NOT EXISTS test_table (
44
- id Int32,
45
- name String,
46
- price Float64,
47
- created_at DateTime,
48
- category String,
49
- active UInt8
50
- ) ENGINE = MergeTree()
51
- ORDER BY id
52
- `;
53
- const CREATE_USERS_TABLE = `
54
- CREATE TABLE IF NOT EXISTS users (
55
- id Int32,
56
- user_name String,
57
- email String,
58
- created_at DateTime,
59
- status String
60
- ) ENGINE = MergeTree()
61
- ORDER BY id
62
- `;
63
- const CREATE_ORDERS_TABLE = `
64
- CREATE TABLE IF NOT EXISTS orders (
65
- id Int32,
66
- user_id Int32,
67
- product_id Int32,
68
- quantity Int32,
69
- total Float64,
70
- status String,
71
- created_at DateTime
72
- ) ENGINE = MergeTree()
73
- ORDER BY id
74
- `;
75
- const CREATE_PRODUCTS_TABLE = `
76
- CREATE TABLE IF NOT EXISTS products (
77
- id Int32,
78
- name String,
79
- price Float64,
80
- category String,
81
- description String
82
- ) ENGINE = MergeTree()
83
- ORDER BY id
84
- `;
85
- // Sample data for tests
86
- export const TEST_DATA = {
87
- test_table: [
88
- { id: 1, name: 'Product 1', price: 10.99, created_at: '2023-01-01 00:00:00', category: 'A', active: 1 },
89
- { id: 2, name: 'Product 2', price: 20.50, created_at: '2023-01-02 00:00:00', category: 'B', active: 1 },
90
- { id: 3, name: 'Product 3', price: 15.75, created_at: '2023-01-03 00:00:00', category: 'A', active: 0 },
91
- { id: 4, name: 'Product 4', price: 25.00, created_at: '2023-01-04 00:00:00', category: 'C', active: 1 },
92
- { id: 5, name: 'Product 5', price: 30.25, created_at: '2023-01-05 00:00:00', category: 'B', active: 0 },
93
- { id: 6, name: 'Product 6', price: 12.99, created_at: '2023-01-06 00:00:00', category: 'A', active: 1 },
94
- { id: 7, name: 'Product 7', price: 22.50, created_at: '2023-01-07 00:00:00', category: 'B', active: 1 },
95
- { id: 8, name: 'Product 8', price: 18.75, created_at: '2023-01-08 00:00:00', category: 'C', active: 0 }
96
- ],
97
- users: [
98
- { id: 1, user_name: 'user1', email: 'user1@example.com', created_at: '2023-01-01 00:00:00', status: 'active' },
99
- { id: 2, user_name: 'user2', email: 'user2@example.com', created_at: '2023-01-02 00:00:00', status: 'active' },
100
- { id: 3, user_name: 'user3', email: 'user3@example.com', created_at: '2023-01-03 00:00:00', status: 'inactive' },
101
- { id: 4, user_name: 'user4', email: 'user4@example.com', created_at: '2023-01-04 00:00:00', status: 'active' },
102
- { id: 5, user_name: 'user5', email: 'user5@example.com', created_at: '2023-01-05 00:00:00', status: 'pending' }
103
- ],
104
- orders: [
105
- { id: 1, user_id: 1, product_id: 1, quantity: 2, total: 21.98, status: 'completed', created_at: '2023-01-10 10:00:00' },
106
- { id: 2, user_id: 1, product_id: 3, quantity: 1, total: 15.75, status: 'completed', created_at: '2023-01-11 11:00:00' },
107
- { id: 3, user_id: 2, product_id: 2, quantity: 3, total: 61.50, status: 'completed', created_at: '2023-01-12 12:00:00' },
108
- { id: 4, user_id: 3, product_id: 5, quantity: 1, total: 30.25, status: 'pending', created_at: '2023-01-13 13:00:00' },
109
- { id: 5, user_id: 4, product_id: 4, quantity: 2, total: 50.00, status: 'completed', created_at: '2023-01-14 14:00:00' },
110
- { id: 6, user_id: 2, product_id: 6, quantity: 1, total: 12.99, status: 'cancelled', created_at: '2023-01-15 15:00:00' },
111
- { id: 7, user_id: 5, product_id: 7, quantity: 4, total: 90.00, status: 'pending', created_at: '2023-01-16 16:00:00' },
112
- { id: 8, user_id: 1, product_id: 8, quantity: 1, total: 18.75, status: 'completed', created_at: '2023-01-17 17:00:00' }
113
- ],
114
- products: [
115
- { id: 1, name: 'Product A', price: 10.99, category: 'Electronics', description: 'A great electronic device' },
116
- { id: 2, name: 'Product B', price: 20.50, category: 'Clothing', description: 'Comfortable clothing item' },
117
- { id: 3, name: 'Product C', price: 15.75, category: 'Electronics', description: 'Another electronic gadget' },
118
- { id: 4, name: 'Product D', price: 25.00, category: 'Home', description: 'Home decoration item' },
119
- { id: 5, name: 'Product E', price: 30.25, category: 'Kitchen', description: 'Useful kitchen tool' },
120
- { id: 6, name: 'Product F', price: 12.99, category: 'Office', description: 'Office supplies' },
121
- { id: 7, name: 'Product G', price: 22.50, category: 'Electronics', description: 'Premium electronic device' },
122
- { id: 8, name: 'Product H', price: 18.75, category: 'Clothing', description: 'Stylish clothing piece' }
123
- ]
124
63
  };
125
- // Helper to set up the test database
126
- export async function setupTestDatabase() {
127
- const client = ClickHouseConnection.getClient();
128
- // Create database if it doesn't exist
129
- await client.command({
130
- query: `CREATE DATABASE IF NOT EXISTS ${CLICKHOUSE_DB}`
131
- });
132
- // Use the test database
133
- await client.command({
134
- query: `USE ${CLICKHOUSE_DB}`
135
- });
136
- // Create test tables
137
- await client.command({
138
- query: CREATE_TEST_TABLE
139
- });
140
- await client.command({
141
- query: CREATE_USERS_TABLE
142
- });
143
- await client.command({
144
- query: CREATE_ORDERS_TABLE
145
- });
146
- await client.command({
147
- query: CREATE_PRODUCTS_TABLE
148
- });
149
- // Truncate tables if they exist
150
- await client.command({
151
- query: `TRUNCATE TABLE IF EXISTS test_table`
152
- });
153
- await client.command({
154
- query: `TRUNCATE TABLE IF EXISTS users`
155
- });
156
- await client.command({
157
- query: `TRUNCATE TABLE IF EXISTS orders`
158
- });
159
- await client.command({
160
- query: `TRUNCATE TABLE IF EXISTS products`
161
- });
162
- // Insert test data
163
- // For test_table
164
- for (const item of TEST_DATA.test_table) {
165
- await client.command({
166
- query: `
167
- INSERT INTO test_table (id, name, price, created_at, category, active)
168
- VALUES (${item.id}, '${item.name}', ${item.price}, '${item.created_at}', '${item.category}', ${item.active})
169
- `
170
- });
64
+ // Helper function to ensure connection is initialized
65
+ export const ensureConnectionInitialized = () => {
66
+ // If connection hasn't been initialized yet, initialize it
67
+ try {
68
+ ClickHouseConnection.getClient();
171
69
  }
172
- // For users
173
- for (const user of TEST_DATA.users) {
174
- await client.command({
175
- query: `
176
- INSERT INTO users (id, user_name, email, created_at, status)
177
- VALUES (${user.id}, '${user.user_name}', '${user.email}', '${user.created_at}', '${user.status}')
178
- `
70
+ catch (error) {
71
+ // If we get "not initialized" error, initialize the connection
72
+ logger.info('Initializing ClickHouse connection...');
73
+ ClickHouseConnection.initialize({
74
+ host: config.host,
75
+ username: config.user,
76
+ password: config.password,
77
+ database: config.database,
179
78
  });
180
79
  }
181
- // For orders
182
- for (const order of TEST_DATA.orders) {
183
- await client.command({
184
- query: `
185
- INSERT INTO orders (id, user_id, product_id, quantity, total, status, created_at)
186
- VALUES (${order.id}, ${order.user_id}, ${order.product_id}, ${order.quantity}, ${order.total}, '${order.status}', '${order.created_at}')
187
- `
188
- });
80
+ return ClickHouseConnection.getClient();
81
+ };
82
+ // Check if Docker is installed
83
+ export const isDockerAvailable = async () => {
84
+ try {
85
+ await execAsync('docker --version');
86
+ return true;
189
87
  }
190
- // For products
191
- for (const product of TEST_DATA.products) {
192
- await client.command({
193
- query: `
194
- INSERT INTO products (id, name, price, category, description)
195
- VALUES (${product.id}, '${product.name}', ${product.price}, '${product.category}', '${product.description}')
196
- `
197
- });
88
+ catch (error) {
89
+ return false;
198
90
  }
199
- }
200
- // Helper to check if Docker is available
201
- export function isDockerAvailable() {
91
+ };
92
+ // Check if Docker Compose is installed
93
+ export const isDockerComposeAvailable = async () => {
202
94
  try {
203
- execSync('docker --version', { stdio: 'ignore' });
95
+ await execAsync('docker compose version');
204
96
  return true;
205
97
  }
206
98
  catch (error) {
207
- return false;
99
+ try {
100
+ // Try the hyphenated version for older installations
101
+ await execAsync('docker-compose --version');
102
+ return true;
103
+ }
104
+ catch {
105
+ return false;
106
+ }
107
+ }
108
+ };
109
+ // Check if a docker container is running
110
+ export const isContainerRunning = async (containerName) => {
111
+ try {
112
+ const { stdout } = await execAsync(`docker ps --filter "name=${containerName}" --format "{{.Names}}"`);
113
+ return stdout.trim() === containerName;
208
114
  }
209
- }
210
- // Helper to start a ClickHouse Docker container for testing
211
- export function startClickHouseContainer() {
212
- if (!isDockerAvailable()) {
213
- console.warn('Docker is not available. Integration tests will use the configured ClickHouse instance.');
214
- return;
115
+ catch (error) {
116
+ return false;
215
117
  }
118
+ };
119
+ // Check if ClickHouse is ready
120
+ export const isClickHouseReady = async () => {
216
121
  try {
217
- // Check if container is already running
218
- const containerId = execSync('docker ps -q -f name=hypequery-test-clickhouse').toString().trim();
219
- if (containerId) {
220
- console.log('ClickHouse test container is already running.');
221
- return;
122
+ const client = ClickHouseConnection.getClient();
123
+ await client.ping();
124
+ return true;
125
+ }
126
+ catch (error) {
127
+ return false;
128
+ }
129
+ };
130
+ // Start the ClickHouse container
131
+ export const startClickHouseContainer = async () => {
132
+ const dockerAvailable = await isDockerAvailable();
133
+ if (!dockerAvailable) {
134
+ throw new Error('Docker is not available. Please install Docker to run integration tests.');
135
+ }
136
+ const composeAvailable = await isDockerComposeAvailable();
137
+ // Use Docker Compose if available
138
+ if (composeAvailable) {
139
+ logger.info('Starting ClickHouse container with Docker Compose...');
140
+ try {
141
+ // Fix the path to the docker-compose.test.yml file
142
+ const composePath = path.resolve(projectRoot, 'packages/clickhouse/docker-compose.test.yml');
143
+ logger.info(`Using Docker Compose file at: ${composePath}`);
144
+ // Make sure we're executing the command from the correct directory
145
+ await execAsync(`docker compose -f "${composePath}" up -d`);
222
146
  }
223
- // Start a new container with the hypequery user already configured
224
- execSync(`docker run -d --name hypequery-test-clickhouse -p 8123:8123 -p 9000:9000 --ulimit nofile=262144:262144 -e CLICKHOUSE_USER=${CLICKHOUSE_USER} -e CLICKHOUSE_PASSWORD=${CLICKHOUSE_PASSWORD} -e CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT=1 clickhouse/clickhouse-server:latest`, { stdio: 'inherit' });
225
- console.log('Started ClickHouse test container with user:', CLICKHOUSE_USER);
226
- // Wait for ClickHouse to be ready
227
- let attempts = 0;
228
- const maxAttempts = 30;
229
- while (attempts < maxAttempts) {
230
- try {
231
- execSync('curl -s http://localhost:8123/ping', { stdio: 'ignore' });
232
- console.log('ClickHouse is ready.');
233
- break;
234
- }
235
- catch (error) {
236
- attempts++;
237
- if (attempts >= maxAttempts) {
238
- throw new Error('ClickHouse failed to start in time.');
239
- }
240
- console.log(`Waiting for ClickHouse to be ready... (${attempts}/${maxAttempts})`);
241
- execSync('sleep 1');
242
- }
147
+ catch (error) {
148
+ logger.error('Failed to start ClickHouse container with Docker Compose:', error);
149
+ throw error;
243
150
  }
244
- // Create the test database
151
+ }
152
+ else {
153
+ // Fallback to Docker run
154
+ logger.info('Starting ClickHouse container with Docker...');
245
155
  try {
246
- execSync(`
247
- docker exec hypequery-test-clickhouse clickhouse-client -u ${CLICKHOUSE_USER} --password ${CLICKHOUSE_PASSWORD} --query "CREATE DATABASE IF NOT EXISTS ${CLICKHOUSE_DB}"
248
- `, { stdio: 'inherit' });
249
- console.log(`Created database '${CLICKHOUSE_DB}'.`);
156
+ await execAsync(`
157
+ docker run -d --name hypequery-test-clickhouse
158
+ -p 8123:8123 -p 9000:9000
159
+ -e CLICKHOUSE_USER=${config.user}
160
+ -e CLICKHOUSE_PASSWORD=${config.password}
161
+ -e CLICKHOUSE_DB=${config.database}
162
+ --ulimit nofile=262144:262144
163
+ clickhouse/clickhouse-server:latest
164
+ `);
250
165
  }
251
166
  catch (error) {
252
- console.error('Failed to create database:', error);
167
+ logger.error('Failed to start ClickHouse container with Docker:', error);
253
168
  throw error;
254
169
  }
255
170
  }
256
- catch (error) {
257
- console.error('Failed to start ClickHouse container:', error);
258
- throw error;
171
+ };
172
+ // Wait for ClickHouse to be ready
173
+ export const waitForClickHouse = async (maxAttempts = 30, retryInterval = 1000) => {
174
+ logger.info('Waiting for ClickHouse to be ready...');
175
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
176
+ if (await isClickHouseReady()) {
177
+ logger.info('ClickHouse is ready!');
178
+ return;
179
+ }
180
+ logger.info(`Waiting for ClickHouse... Attempt ${attempt}/${maxAttempts}`);
181
+ await new Promise(resolve => setTimeout(resolve, retryInterval));
182
+ }
183
+ throw new Error(`ClickHouse failed to start after ${maxAttempts} attempts`);
184
+ };
185
+ // Stop the ClickHouse container
186
+ export const stopClickHouseContainer = async () => {
187
+ const composeAvailable = await isDockerComposeAvailable();
188
+ if (composeAvailable) {
189
+ logger.info('Stopping ClickHouse container with Docker Compose...');
190
+ try {
191
+ // Fix the path to the docker-compose.test.yml file
192
+ const composePath = path.resolve(projectRoot, 'packages/clickhouse/docker-compose.test.yml');
193
+ logger.info(`Using Docker Compose file at: ${composePath}`);
194
+ // Make sure we're executing the command from the correct directory
195
+ await execAsync(`docker compose -f "${composePath}" down -v`);
196
+ }
197
+ catch (error) {
198
+ logger.error('Failed to stop ClickHouse container with Docker Compose:', error);
199
+ // Log the error but don't throw, so the tests can complete
200
+ // This allows for manual cleanup if needed
201
+ }
259
202
  }
260
- }
261
- // Helper to stop the ClickHouse Docker container
262
- export function stopClickHouseContainer() {
263
- if (!isDockerAvailable()) {
264
- return;
203
+ else {
204
+ logger.info('Stopping ClickHouse container with Docker...');
205
+ try {
206
+ await execAsync('docker stop hypequery-test-clickhouse && docker rm hypequery-test-clickhouse');
207
+ }
208
+ catch (error) {
209
+ logger.error('Failed to stop ClickHouse container with Docker:', error);
210
+ // Log the error but don't throw, so the tests can complete
211
+ }
265
212
  }
213
+ };
214
+ // Test data
215
+ export const TEST_DATA = {
216
+ test_table: [
217
+ { id: 1, name: 'Product A', category: 'A', price: 10.5, created_at: '2023-01-01', is_active: true },
218
+ { id: 2, name: 'Product B', category: 'B', price: 20.75, created_at: '2023-01-02', is_active: true },
219
+ { id: 3, name: 'Product C', category: 'A', price: 15.0, created_at: '2023-01-03', is_active: false },
220
+ { id: 4, name: 'Product D', category: 'C', price: 8.25, created_at: '2023-01-04', is_active: true },
221
+ { id: 5, name: 'Product E', category: 'B', price: 30.0, created_at: '2023-01-05', is_active: true },
222
+ ],
223
+ users: [
224
+ { id: 1, user_name: 'john_doe', email: 'john@example.com', status: 'active', created_at: '2023-01-01' },
225
+ { id: 2, user_name: 'jane_smith', email: 'jane@example.com', status: 'active', created_at: '2023-01-02' },
226
+ { id: 3, user_name: 'bob_jones', email: 'bob@example.com', status: 'inactive', created_at: '2023-01-03' },
227
+ ],
228
+ orders: [
229
+ { id: 1, user_id: 1, product_id: 1, quantity: 2, total: 21.0, status: 'completed', created_at: '2023-01-10' },
230
+ { id: 2, user_id: 1, product_id: 3, quantity: 1, total: 15.0, status: 'completed', created_at: '2023-01-11' },
231
+ { id: 3, user_id: 2, product_id: 2, quantity: 3, total: 62.25, status: 'pending', created_at: '2023-01-12' },
232
+ { id: 4, user_id: 2, product_id: 5, quantity: 1, total: 30.0, status: 'completed', created_at: '2023-01-13' },
233
+ { id: 5, user_id: 3, product_id: 4, quantity: 2, total: 16.5, status: 'cancelled', created_at: '2023-01-14' },
234
+ ],
235
+ };
236
+ // Setup the test database
237
+ export const setupTestDatabase = async () => {
238
+ // Make sure connection is initialized before getting client
239
+ const client = ensureConnectionInitialized();
266
240
  try {
267
- execSync('docker stop hypequery-test-clickhouse', { stdio: 'ignore' });
268
- execSync('docker rm hypequery-test-clickhouse', { stdio: 'ignore' });
269
- console.log('Stopped and removed ClickHouse test container.');
241
+ // Create and use database if it doesn't exist
242
+ await client.exec({ query: `CREATE DATABASE IF NOT EXISTS ${config.database}` });
243
+ await client.exec({ query: `USE ${config.database}` });
244
+ // Drop tables if they exist
245
+ await client.exec({ query: 'DROP TABLE IF EXISTS test_table' });
246
+ await client.exec({ query: 'DROP TABLE IF EXISTS users' });
247
+ await client.exec({ query: 'DROP TABLE IF EXISTS orders' });
248
+ // Create tables
249
+ await client.exec({
250
+ query: `
251
+ CREATE TABLE test_table (
252
+ id UInt32,
253
+ name String,
254
+ category String,
255
+ price Float64,
256
+ created_at Date,
257
+ is_active Boolean
258
+ ) ENGINE = MergeTree()
259
+ ORDER BY id
260
+ `
261
+ });
262
+ await client.exec({
263
+ query: `
264
+ CREATE TABLE users (
265
+ id UInt32,
266
+ user_name String,
267
+ email String,
268
+ status String,
269
+ created_at Date
270
+ ) ENGINE = MergeTree()
271
+ ORDER BY id
272
+ `
273
+ });
274
+ await client.exec({
275
+ query: `
276
+ CREATE TABLE orders (
277
+ id UInt32,
278
+ user_id UInt32,
279
+ product_id UInt32,
280
+ quantity UInt32,
281
+ total Float64,
282
+ status String,
283
+ created_at Date
284
+ ) ENGINE = MergeTree()
285
+ ORDER BY id
286
+ `
287
+ });
288
+ // Insert test data
289
+ for (const row of TEST_DATA.test_table) {
290
+ await client.insert({
291
+ table: 'test_table',
292
+ values: [row],
293
+ format: 'JSONEachRow'
294
+ });
295
+ }
296
+ for (const row of TEST_DATA.users) {
297
+ await client.insert({
298
+ table: 'users',
299
+ values: [row],
300
+ format: 'JSONEachRow'
301
+ });
302
+ }
303
+ for (const row of TEST_DATA.orders) {
304
+ await client.insert({
305
+ table: 'orders',
306
+ values: [row],
307
+ format: 'JSONEachRow'
308
+ });
309
+ }
310
+ logger.info('Test database setup complete');
270
311
  }
271
312
  catch (error) {
272
- console.error('Failed to stop ClickHouse container:', error);
313
+ logger.error('Failed to set up test database:', error);
314
+ throw error;
273
315
  }
274
- }
316
+ };
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Common initialization for all integration tests
3
+ * Import this at the beginning of each test file to ensure consistent setup
4
+ */
5
+ export declare const skipIntegrationTests: () => boolean;
6
+ export declare const initializeForTest: () => Promise<void>;
7
+ //# sourceMappingURL=test-initializer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-initializer.d.ts","sourceRoot":"","sources":["../../../../src/core/tests/integration/test-initializer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AASH,eAAO,MAAM,oBAAoB,eAEhC,CAAC;AAEF,eAAO,MAAM,iBAAiB,QAAa,OAAO,CAAC,IAAI,CAqBtD,CAAC"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Common initialization for all integration tests
3
+ * Import this at the beginning of each test file to ensure consistent setup
4
+ */
5
+ import { startClickHouseContainer, waitForClickHouse, ensureConnectionInitialized, setupTestDatabase } from './setup';
6
+ export const skipIntegrationTests = () => {
7
+ return process.env.SKIP_INTEGRATION_TESTS === 'true' || process.env.CI === 'true';
8
+ };
9
+ export const initializeForTest = async () => {
10
+ if (skipIntegrationTests()) {
11
+ return;
12
+ }
13
+ try {
14
+ // Initialize the connection
15
+ ensureConnectionInitialized();
16
+ // Make sure container is running
17
+ await startClickHouseContainer();
18
+ // Wait for ClickHouse to be ready
19
+ await waitForClickHouse();
20
+ // Set up the test database
21
+ await setupTestDatabase();
22
+ }
23
+ catch (error) {
24
+ console.error('Failed to initialize test environment:', error);
25
+ throw error;
26
+ }
27
+ };
28
+ // Automatically initialize when this module is imported
29
+ initializeForTest().catch(error => {
30
+ console.error('Test initialization failed:', error);
31
+ process.exit(1);
32
+ });
@@ -0,0 +1,30 @@
1
+ import { QueryBuilder } from '../query-builder';
2
+ type ColumnType = 'Int32' | 'String' | 'Float64' | 'Date' | 'UInt8';
3
+ export type TestTableSchema = {
4
+ id: 'Int32';
5
+ name: 'String';
6
+ price: 'Float64';
7
+ created_at: 'Date';
8
+ category: 'String';
9
+ active: 'UInt8';
10
+ created_by: 'Int32';
11
+ updated_by: 'Int32';
12
+ };
13
+ export type UsersSchema = {
14
+ id: 'Int32';
15
+ user_name: 'String';
16
+ email: 'String';
17
+ created_at: 'Date';
18
+ };
19
+ export interface TestSchema {
20
+ test_table: TestTableSchema;
21
+ users: UsersSchema;
22
+ [tableName: string]: {
23
+ [columnName: string]: ColumnType;
24
+ };
25
+ }
26
+ export declare const TEST_SCHEMAS: TestSchema;
27
+ export declare function setupUsersBuilder(): QueryBuilder<TestSchema, TestSchema['users'], false, {}, TestSchema['users']>;
28
+ export declare function setupTestBuilder(): QueryBuilder<TestSchema, TestSchema['test_table'], false, {}, TestSchema['test_table']>;
29
+ export {};
30
+ //# sourceMappingURL=test-utils.d.ts.map
@@ -0,0 +1,37 @@
1
+ export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
2
+ export interface QueryLog {
3
+ query: string;
4
+ parameters?: any[];
5
+ startTime: number;
6
+ endTime?: number;
7
+ duration?: number;
8
+ status: 'started' | 'completed' | 'error';
9
+ error?: Error;
10
+ rowCount?: number;
11
+ queryId?: string;
12
+ }
13
+ export interface LoggerOptions {
14
+ level?: LogLevel;
15
+ enabled?: boolean;
16
+ onQueryLog?: (log: QueryLog) => void;
17
+ }
18
+ declare class Logger {
19
+ private static instance;
20
+ private level;
21
+ private enabled;
22
+ private onQueryLog?;
23
+ private querySubscribers;
24
+ private constructor();
25
+ static getInstance(): Logger;
26
+ configure(options: LoggerOptions): void;
27
+ subscribeToQuery(queryId: string, callback: (log: QueryLog) => void): () => void;
28
+ private shouldLog;
29
+ debug(message: string, ...args: any[]): void;
30
+ info(message: string, ...args: any[]): void;
31
+ warn(message: string, ...args: any[]): void;
32
+ error(message: string, ...args: any[]): void;
33
+ logQuery(log: QueryLog): void;
34
+ }
35
+ export declare const logger: Logger;
36
+ export {};
37
+ //# sourceMappingURL=logger.d.ts.map