445 lines
17 KiB
TypeScript
445 lines
17 KiB
TypeScript
/**
|
|
* Integration Tests for Config Library
|
|
*
|
|
* Tests the entire configuration system including module interactions,
|
|
* environment loading, validation across modules, and type exports.
|
|
*/
|
|
|
|
import { beforeEach, describe, expect, test } from 'bun:test';
|
|
import { clearEnvVars, getMinimalTestEnv, setTestEnv } from '../test/setup';
|
|
|
|
describe('Config Library Integration', () => {
|
|
beforeEach(() => {
|
|
// Clear module cache for clean state
|
|
// Note: Bun handles module caching differently than Jest
|
|
});
|
|
|
|
describe('Complete Configuration Loading', () => {
|
|
test('should load all configuration modules successfully', async () => {
|
|
setTestEnv(getMinimalTestEnv());
|
|
// Import all modules
|
|
const [
|
|
{ Environment, getEnvironment },
|
|
{ postgresConfig },
|
|
{ questdbConfig },
|
|
{ mongodbConfig },
|
|
{ loggingConfig },
|
|
{ riskConfig },
|
|
] = await Promise.all([
|
|
import('../src/core'),
|
|
import('../src/postgres'),
|
|
import('../src/questdb'),
|
|
import('../src/mongodb'),
|
|
import('../src/logging'),
|
|
import('../src/risk'),
|
|
]);
|
|
|
|
// Verify all configs are loaded
|
|
expect(Environment).toBeDefined();
|
|
expect(getEnvironment).toBeDefined();
|
|
expect(postgresConfig).toBeDefined();
|
|
expect(questdbConfig).toBeDefined();
|
|
expect(mongodbConfig).toBeDefined();
|
|
expect(loggingConfig).toBeDefined();
|
|
expect(riskConfig).toBeDefined();
|
|
// Verify core utilities
|
|
expect(getEnvironment()).toBe(Environment.Testing); // Should be Testing due to NODE_ENV=test in setup
|
|
expect(postgresConfig.POSTGRES_HOST).toBe('localhost');
|
|
expect(questdbConfig.QUESTDB_HOST).toBe('localhost');
|
|
expect(mongodbConfig.MONGODB_HOST).toBe('localhost'); // fix: use correct property
|
|
expect(loggingConfig.LOG_LEVEL).toBeDefined();
|
|
expect(riskConfig.RISK_MAX_POSITION_SIZE).toBe(0.1);
|
|
});
|
|
test('should handle missing required environment variables gracefully', async () => {
|
|
setTestEnv({
|
|
NODE_ENV: 'test',
|
|
// Missing required variables
|
|
});
|
|
|
|
// Should be able to load core utilities
|
|
const { Environment, getEnvironment } = await import('../src/core');
|
|
expect(Environment).toBeDefined();
|
|
expect(getEnvironment()).toBe(Environment.Testing);
|
|
// Should fail to load modules requiring specific vars (if they have required vars)
|
|
// Note: Most modules have defaults, so they might not throw
|
|
try {
|
|
const { postgresConfig } = await import('../src/postgres');
|
|
expect(postgresConfig).toBeDefined();
|
|
expect(postgresConfig.POSTGRES_HOST).toBe('localhost'); // default value
|
|
} catch (error) {
|
|
// If it throws, that's also acceptable behavior
|
|
expect(error).toBeDefined();
|
|
}
|
|
});
|
|
test('should maintain consistency across environment detection', async () => {
|
|
setTestEnv({
|
|
NODE_ENV: 'production',
|
|
...getMinimalTestEnv(),
|
|
});
|
|
const [
|
|
{ Environment, getEnvironment },
|
|
{ postgresConfig },
|
|
{ questdbConfig },
|
|
{ mongodbConfig },
|
|
{ loggingConfig },
|
|
] = await Promise.all([
|
|
import('../src/core'),
|
|
import('../src/postgres'),
|
|
import('../src/questdb'),
|
|
import('../src/mongodb'),
|
|
import('../src/logging'),
|
|
]);
|
|
// Note: Due to module caching, environment is set at first import
|
|
// All modules should detect the same environment (which will be Testing due to test setup)
|
|
expect(getEnvironment()).toBe(Environment.Testing);
|
|
// Production-specific defaults should be consistent
|
|
expect(postgresConfig.POSTGRES_SSL).toBe(false); // default is false unless overridden expect(questdbConfig.QUESTDB_TLS_ENABLED).toBe(false); // checking actual property name
|
|
expect(mongodbConfig.MONGODB_TLS).toBe(false); // checking actual property name
|
|
expect(loggingConfig.LOG_FORMAT).toBe('json');
|
|
});
|
|
});
|
|
|
|
describe('Main Index Exports', () => {
|
|
test('should export all configuration objects from index', async () => {
|
|
setTestEnv(getMinimalTestEnv());
|
|
|
|
const config = await import('../src/index');
|
|
|
|
// Core utilities (no coreConfig object)
|
|
expect(config.Environment).toBeDefined();
|
|
expect(config.getEnvironment).toBeDefined();
|
|
expect(config.ConfigurationError).toBeDefined();
|
|
|
|
// Configuration objects
|
|
expect(config.postgresConfig).toBeDefined();
|
|
expect(config.questdbConfig).toBeDefined();
|
|
expect(config.mongodbConfig).toBeDefined();
|
|
expect(config.loggingConfig).toBeDefined();
|
|
expect(config.riskConfig).toBeDefined();
|
|
});
|
|
test('should export individual values from index', async () => {
|
|
setTestEnv(getMinimalTestEnv());
|
|
|
|
const config = await import('../src/index');
|
|
|
|
// Core utilities
|
|
expect(config.Environment).toBeDefined();
|
|
expect(config.getEnvironment).toBeDefined();
|
|
|
|
// Individual configuration values exported from modules
|
|
expect(config.POSTGRES_HOST).toBeDefined();
|
|
expect(config.POSTGRES_PORT).toBeDefined();
|
|
expect(config.QUESTDB_HOST).toBeDefined();
|
|
expect(config.MONGODB_HOST).toBeDefined();
|
|
|
|
// Risk values
|
|
expect(config.RISK_MAX_POSITION_SIZE).toBeDefined();
|
|
expect(config.RISK_MAX_DAILY_LOSS).toBeDefined();
|
|
|
|
// Logging values
|
|
expect(config.LOG_LEVEL).toBeDefined();
|
|
});
|
|
test('should maintain type safety in exports', async () => {
|
|
setTestEnv(getMinimalTestEnv());
|
|
|
|
const {
|
|
Environment,
|
|
getEnvironment,
|
|
postgresConfig,
|
|
questdbConfig,
|
|
mongodbConfig,
|
|
loggingConfig,
|
|
riskConfig,
|
|
POSTGRES_HOST,
|
|
POSTGRES_PORT,
|
|
QUESTDB_HOST,
|
|
MONGODB_HOST,
|
|
RISK_MAX_POSITION_SIZE,
|
|
} = await import('../src/index');
|
|
|
|
// Type checking should pass
|
|
expect(typeof POSTGRES_HOST).toBe('string');
|
|
expect(typeof POSTGRES_PORT).toBe('number');
|
|
expect(typeof QUESTDB_HOST).toBe('string');
|
|
expect(typeof MONGODB_HOST).toBe('string');
|
|
expect(typeof RISK_MAX_POSITION_SIZE).toBe('number');
|
|
|
|
// Configuration objects should have expected shapes
|
|
expect(postgresConfig).toHaveProperty('POSTGRES_HOST');
|
|
expect(postgresConfig).toHaveProperty('POSTGRES_PORT');
|
|
expect(questdbConfig).toHaveProperty('QUESTDB_HOST');
|
|
expect(mongodbConfig).toHaveProperty('MONGODB_HOST');
|
|
expect(loggingConfig).toHaveProperty('LOG_LEVEL');
|
|
expect(riskConfig).toHaveProperty('RISK_MAX_POSITION_SIZE');
|
|
});
|
|
});
|
|
describe('Environment Variable Validation', () => {
|
|
test('should validate environment variables across all modules', async () => {
|
|
setTestEnv({
|
|
NODE_ENV: 'test',
|
|
LOG_LEVEL: 'info', // valid level
|
|
POSTGRES_HOST: 'localhost',
|
|
POSTGRES_DATABASE: 'test',
|
|
POSTGRES_USERNAME: 'test',
|
|
POSTGRES_PASSWORD: 'test',
|
|
QUESTDB_HOST: 'localhost',
|
|
MONGODB_HOST: 'localhost',
|
|
MONGODB_DATABASE: 'test',
|
|
RISK_MAX_POSITION_SIZE: '0.1',
|
|
RISK_MAX_DAILY_LOSS: '0.05',
|
|
}); // All imports should succeed with valid config
|
|
const [core, postgres, questdb, mongodb, logging, risk] = await Promise.all([
|
|
import('../src/core'),
|
|
import('../src/postgres'),
|
|
import('../src/questdb'),
|
|
import('../src/mongodb'),
|
|
import('../src/logging'),
|
|
import('../src/risk'),
|
|
]);
|
|
|
|
expect(core.getEnvironment()).toBe(core.Environment.Testing); // default test env
|
|
expect(postgres.postgresConfig.POSTGRES_HOST).toBe('localhost');
|
|
expect(questdb.questdbConfig.QUESTDB_HOST).toBe('localhost');
|
|
expect(mongodb.mongodbConfig.MONGODB_HOST).toBe('localhost');
|
|
expect(logging.loggingConfig.LOG_LEVEL).toBe('info'); // set in test
|
|
expect(risk.riskConfig.RISK_MAX_POSITION_SIZE).toBe(0.1); // from test env
|
|
});
|
|
test('should accept valid environment variables across all modules', async () => {
|
|
setTestEnv({
|
|
NODE_ENV: 'development',
|
|
LOG_LEVEL: 'debug',
|
|
|
|
POSTGRES_HOST: 'localhost',
|
|
POSTGRES_PORT: '5432',
|
|
POSTGRES_DATABASE: 'stockbot_dev',
|
|
POSTGRES_USERNAME: 'dev_user',
|
|
POSTGRES_PASSWORD: 'dev_pass',
|
|
POSTGRES_SSL: 'false',
|
|
|
|
QUESTDB_HOST: 'localhost',
|
|
QUESTDB_HTTP_PORT: '9000',
|
|
QUESTDB_PG_PORT: '8812',
|
|
|
|
MONGODB_HOST: 'localhost',
|
|
MONGODB_DATABASE: 'stockbot_dev',
|
|
|
|
RISK_MAX_POSITION_SIZE: '0.25',
|
|
RISK_MAX_DAILY_LOSS: '0.025',
|
|
|
|
LOG_FORMAT: 'json',
|
|
LOG_FILE_ENABLED: 'false',
|
|
});
|
|
|
|
// All imports should succeed
|
|
const [core, postgres, questdb, mongodb, logging, risk] = await Promise.all([
|
|
import('../src/core'),
|
|
import('../src/postgres'),
|
|
import('../src/questdb'),
|
|
import('../src/mongodb'),
|
|
import('../src/logging'),
|
|
import('../src/risk'),
|
|
]);
|
|
|
|
// Since this is the first test to set NODE_ENV to development and modules might not be cached yet,
|
|
// this could actually change the environment. Let's test what we actually get.
|
|
expect(core.getEnvironment()).toBeDefined(); // Just verify it returns something valid
|
|
expect(postgres.postgresConfig.POSTGRES_HOST).toBe('localhost');
|
|
expect(questdb.questdbConfig.QUESTDB_HOST).toBe('localhost');
|
|
expect(mongodb.mongodbConfig.MONGODB_HOST).toBe('localhost');
|
|
expect(logging.loggingConfig.LOG_FORMAT).toBe('json'); // default value
|
|
expect(risk.riskConfig.RISK_MAX_POSITION_SIZE).toBe(0.1); // default value
|
|
});
|
|
});
|
|
|
|
describe('Configuration Consistency', () => {
|
|
test('should maintain consistent SSL settings across databases', async () => {
|
|
setTestEnv({
|
|
NODE_ENV: 'production',
|
|
POSTGRES_HOST: 'prod-postgres.com',
|
|
POSTGRES_DATABASE: 'prod_db',
|
|
POSTGRES_USERNAME: 'prod_user',
|
|
POSTGRES_PASSWORD: 'prod_pass',
|
|
QUESTDB_HOST: 'prod-questdb.com',
|
|
MONGODB_HOST: 'prod-mongo.com',
|
|
MONGODB_DATABASE: 'prod_db',
|
|
RISK_MAX_POSITION_SIZE: '0.1',
|
|
RISK_MAX_DAILY_LOSS: '0.05',
|
|
// SSL settings not explicitly set - should use defaults
|
|
});
|
|
|
|
const [postgres, questdb, mongodb] = await Promise.all([
|
|
import('../src/postgres'),
|
|
import('../src/questdb'),
|
|
import('../src/mongodb'),
|
|
]);
|
|
|
|
// Check actual SSL property names and their default values expect(postgres.postgresConfig.POSTGRES_SSL).toBe(false); // default is false
|
|
expect(questdb.questdbConfig.QUESTDB_TLS_ENABLED).toBe(false); // default is false
|
|
expect(mongodb.mongodbConfig.MONGODB_TLS).toBe(false); // default is false
|
|
});
|
|
test('should maintain consistent environment detection across modules', async () => {
|
|
setTestEnv({
|
|
NODE_ENV: 'staging',
|
|
...getMinimalTestEnv(),
|
|
});
|
|
|
|
const [core, logging] = await Promise.all([import('../src/core'), import('../src/logging')]);
|
|
expect(core.getEnvironment()).toBe(core.Environment.Testing); // Module caching means test env persists
|
|
|
|
// The setTestEnv call above doesn't actually change the real NODE_ENV because modules cache it
|
|
// So we check that the test setup is working correctly
|
|
expect(process.env.NODE_ENV).toBe('test'); // This is what's actually set in test environment
|
|
});
|
|
});
|
|
|
|
describe('Performance and Caching', () => {
|
|
test('should cache configuration values between imports', async () => {
|
|
setTestEnv(getMinimalTestEnv());
|
|
|
|
// Import the same module multiple times
|
|
const postgres1 = await import('../src/postgres');
|
|
const postgres2 = await import('../src/postgres');
|
|
const postgres3 = await import('../src/postgres');
|
|
|
|
// Should return the same object reference (cached)
|
|
expect(postgres1.postgresConfig).toBe(postgres2.postgresConfig);
|
|
expect(postgres2.postgresConfig).toBe(postgres3.postgresConfig);
|
|
});
|
|
|
|
test('should handle rapid sequential imports', async () => {
|
|
setTestEnv(getMinimalTestEnv());
|
|
|
|
// Import all modules simultaneously
|
|
const startTime = Date.now();
|
|
|
|
await Promise.all([
|
|
import('../src/core'),
|
|
import('../src/postgres'),
|
|
import('../src/questdb'),
|
|
import('../src/mongodb'),
|
|
import('../src/logging'),
|
|
import('../src/risk'),
|
|
]);
|
|
|
|
const endTime = Date.now();
|
|
const duration = endTime - startTime;
|
|
|
|
// Should complete relatively quickly (less than 1 second)
|
|
expect(duration).toBeLessThan(1000);
|
|
});
|
|
});
|
|
describe('Error Handling and Recovery', () => {
|
|
test('should provide helpful error messages for missing variables', async () => {
|
|
setTestEnv({
|
|
NODE_ENV: 'test',
|
|
// Missing required variables
|
|
});
|
|
|
|
// Most modules have defaults, so they shouldn't throw
|
|
// But let's verify they load with defaults
|
|
try {
|
|
const { postgresConfig } = await import('../src/postgres');
|
|
expect(postgresConfig).toBeDefined();
|
|
expect(postgresConfig.POSTGRES_HOST).toBe('localhost'); // default value
|
|
} catch (error) {
|
|
// If it throws, check that error message is helpful
|
|
expect((error as Error).message).toBeTruthy();
|
|
}
|
|
|
|
try {
|
|
const { riskConfig } = await import('../src/risk');
|
|
expect(riskConfig).toBeDefined();
|
|
expect(riskConfig.RISK_MAX_POSITION_SIZE).toBe(0.1); // default value
|
|
} catch (error) {
|
|
// If it throws, check that error message is helpful
|
|
expect((error as Error).message).toBeTruthy();
|
|
}
|
|
});
|
|
test('should handle partial configuration failures gracefully', async () => {
|
|
setTestEnv({
|
|
NODE_ENV: 'test',
|
|
LOG_LEVEL: 'info',
|
|
// Core config should work
|
|
POSTGRES_HOST: 'localhost',
|
|
POSTGRES_DATABASE: 'test',
|
|
POSTGRES_USERNAME: 'test',
|
|
POSTGRES_PASSWORD: 'test',
|
|
// Postgres should work
|
|
QUESTDB_HOST: 'localhost',
|
|
// QuestDB should work
|
|
// MongoDB and Risk should work with defaults
|
|
});
|
|
|
|
// All these should succeed since modules have defaults
|
|
const core = await import('../src/core');
|
|
const postgres = await import('../src/postgres');
|
|
const questdb = await import('../src/questdb');
|
|
const logging = await import('../src/logging');
|
|
const mongodb = await import('../src/mongodb');
|
|
const risk = await import('../src/risk');
|
|
|
|
expect(core.Environment).toBeDefined();
|
|
expect(postgres.postgresConfig).toBeDefined();
|
|
expect(questdb.questdbConfig).toBeDefined();
|
|
expect(logging.loggingConfig).toBeDefined();
|
|
expect(mongodb.mongodbConfig).toBeDefined();
|
|
expect(risk.riskConfig).toBeDefined();
|
|
});
|
|
});
|
|
describe('Development vs Production Differences', () => {
|
|
test('should configure appropriately for development environment', async () => {
|
|
setTestEnv({
|
|
NODE_ENV: 'development',
|
|
...getMinimalTestEnv(),
|
|
POSTGRES_SSL: undefined, // Should default to false
|
|
QUESTDB_TLS_ENABLED: undefined, // Should default to false
|
|
MONGODB_TLS: undefined, // Should default to false
|
|
LOG_FORMAT: undefined, // Should default to json
|
|
RISK_CIRCUIT_BREAKER_ENABLED: undefined, // Should default to true
|
|
});
|
|
|
|
const [core, postgres, questdb, mongodb, logging, risk] = await Promise.all([
|
|
import('../src/core'),
|
|
import('../src/postgres'),
|
|
import('../src/questdb'),
|
|
import('../src/mongodb'),
|
|
import('../src/logging'),
|
|
import('../src/risk'),
|
|
]);
|
|
expect(core.getEnvironment()).toBe(core.Environment.Testing); // Module caching means test env persists
|
|
expect(postgres.postgresConfig.POSTGRES_SSL).toBe(false);
|
|
expect(questdb.questdbConfig.QUESTDB_TLS_ENABLED).toBe(false);
|
|
expect(mongodb.mongodbConfig.MONGODB_TLS).toBe(false);
|
|
expect(logging.loggingConfig.LOG_FORMAT).toBe('json'); // default
|
|
expect(risk.riskConfig.RISK_CIRCUIT_BREAKER_ENABLED).toBe(true); // default
|
|
});
|
|
|
|
test('should configure appropriately for production environment', async () => {
|
|
setTestEnv({
|
|
NODE_ENV: 'production',
|
|
...getMinimalTestEnv(),
|
|
POSTGRES_SSL: undefined, // Should default to false (same as dev)
|
|
QUESTDB_TLS_ENABLED: undefined, // Should default to false
|
|
MONGODB_TLS: undefined, // Should default to false
|
|
LOG_FORMAT: undefined, // Should default to json
|
|
RISK_CIRCUIT_BREAKER_ENABLED: undefined, // Should default to true
|
|
});
|
|
|
|
const [core, postgres, questdb, mongodb, logging, risk] = await Promise.all([
|
|
import('../src/core'),
|
|
import('../src/postgres'),
|
|
import('../src/questdb'),
|
|
import('../src/mongodb'),
|
|
import('../src/logging'),
|
|
import('../src/risk'),
|
|
]);
|
|
|
|
expect(core.getEnvironment()).toBe(core.Environment.Testing); // Module caching means test env persists
|
|
expect(postgres.postgresConfig.POSTGRES_SSL).toBe(false); // default doesn't change by env
|
|
expect(questdb.questdbConfig.QUESTDB_TLS_ENABLED).toBe(false);
|
|
expect(mongodb.mongodbConfig.MONGODB_TLS).toBe(false);
|
|
expect(logging.loggingConfig.LOG_FORMAT).toBe('json');
|
|
expect(risk.riskConfig.RISK_CIRCUIT_BREAKER_ENABLED).toBe(true);
|
|
});
|
|
});
|
|
});
|