stock-bot/libs/core/config/test/config.test.ts
2025-06-26 16:12:27 -04:00

251 lines
6.9 KiB
TypeScript

import { beforeEach, describe, expect, it } from 'bun:test';
import { z } from 'zod';
import { baseAppSchema, ConfigManager, createAppConfig } from '../src';
import { ConfigError, ConfigValidationError } from '../src/errors';
// Mock loader for testing
class MockLoader {
constructor(
private data: Record<string, unknown>,
public priority: number = 0
) {}
load(): Record<string, unknown> {
return this.data;
}
}
describe('ConfigManager', () => {
let manager: ConfigManager<any>;
beforeEach(() => {
manager = new ConfigManager();
});
it('should initialize with default loaders', () => {
expect(manager).toBeDefined();
});
it('should detect environment', () => {
const env = manager.getEnvironment();
expect(['development', 'test', 'production']).toContain(env);
});
it('should throw when getting config before initialization', () => {
expect(() => manager.get()).toThrow(ConfigError);
});
it('should initialize config with schema', () => {
const schema = z.object({
name: z.string(),
port: z.number(),
});
const mockManager = new ConfigManager({
loaders: [new MockLoader({ name: 'test', port: 3000 })],
});
const config = mockManager.initialize(schema);
expect(config).toEqual({ name: 'test', port: 3000 });
});
it('should merge configs from multiple loaders', () => {
const mockManager = new ConfigManager({
loaders: [
new MockLoader({ name: 'test', port: 3000 }, 1),
new MockLoader({ port: 4000, debug: true }, 2),
],
});
const config = mockManager.initialize();
expect(config).toEqual({ name: 'test', port: 4000, debug: true, environment: 'test' });
});
it('should deep merge nested objects', () => {
const mockManager = new ConfigManager({
loaders: [
new MockLoader({ db: { host: 'localhost', port: 5432 } }, 1),
new MockLoader({ db: { port: 5433, user: 'admin' } }, 2),
],
});
const config = mockManager.initialize();
expect(config).toEqual({
db: { host: 'localhost', port: 5433, user: 'admin' },
environment: 'test',
});
});
it('should get value by path', () => {
const mockManager = new ConfigManager({
loaders: [new MockLoader({ db: { host: 'localhost', port: 5432 } })],
});
mockManager.initialize();
expect(mockManager.getValue('db.host')).toBe('localhost');
expect(mockManager.getValue('db.port')).toBe(5432);
});
it('should throw for non-existent path', () => {
const mockManager = new ConfigManager({
loaders: [new MockLoader({ db: { host: 'localhost' } })],
});
mockManager.initialize();
expect(() => mockManager.getValue('db.password')).toThrow(ConfigError);
});
it('should check if path exists', () => {
const mockManager = new ConfigManager({
loaders: [new MockLoader({ db: { host: 'localhost' } })],
});
mockManager.initialize();
expect(mockManager.has('db.host')).toBe(true);
expect(mockManager.has('db.password')).toBe(false);
});
it('should update config at runtime', () => {
const mockManager = new ConfigManager({
loaders: [new MockLoader({ name: 'test', port: 3000 })],
});
mockManager.initialize();
mockManager.set({ port: 4000 });
expect(mockManager.get()).toEqual({ name: 'test', port: 4000, environment: 'test' });
});
it('should validate config update with schema', () => {
const schema = z.object({
name: z.string(),
port: z.number(),
});
const mockManager = new ConfigManager({
loaders: [new MockLoader({ name: 'test', port: 3000 })],
});
mockManager.initialize(schema);
expect(() => mockManager.set({ port: 'invalid' as any })).toThrow(ConfigValidationError);
});
it('should reset config', () => {
const mockManager = new ConfigManager({
loaders: [new MockLoader({ name: 'test' })],
});
mockManager.initialize();
expect(mockManager.get()).toEqual({ name: 'test', environment: 'test' });
mockManager.reset();
expect(() => mockManager.get()).toThrow(ConfigError);
});
it('should validate against schema', () => {
const schema = z.object({
name: z.string(),
port: z.number(),
});
const mockManager = new ConfigManager({
loaders: [new MockLoader({ name: 'test', port: 3000 })],
});
mockManager.initialize();
const validated = mockManager.validate(schema);
expect(validated).toEqual({ name: 'test', port: 3000 });
});
it('should add environment if not present', () => {
const mockManager = new ConfigManager({
environment: 'test',
loaders: [new MockLoader({ name: 'test' })],
});
const config = mockManager.initialize();
expect(config).toEqual({ name: 'test', environment: 'test' });
});
});
describe('Config Builders', () => {
it('should create app config with schema', () => {
const schema = z.object({
app: z.string(),
version: z.number(),
});
const config = createAppConfig(schema, {
loaders: [new MockLoader({ app: 'myapp', version: 1 })],
});
expect(config).toBeDefined();
});
it('should initialize app config in one step', () => {
const schema = z.object({
app: z.string(),
version: z.number(),
});
const configManager = createAppConfig(schema, {
loaders: [new MockLoader({ app: 'myapp', version: 1 })],
});
const config = configManager.initialize(schema);
expect(config).toEqual({ app: 'myapp', version: 1 });
});
});
describe('Environment Detection', () => {
it('should detect environments correctly in ConfigManager', () => {
// Test with different environments using mock configs
const envConfigs = [{ env: 'development' }, { env: 'production' }, { env: 'test' }];
for (const { env } of envConfigs) {
const mockConfig = {
name: 'test-app',
version: '1.0.0',
environment: env as 'development' | 'production' | 'test',
service: {
name: 'test',
port: 3000,
},
database: {
mongodb: {
uri: 'mongodb://localhost',
database: 'test-db',
},
postgres: {
host: 'localhost',
port: 5432,
database: 'test-db',
user: 'test-user',
password: 'test-pass',
},
questdb: {
host: 'localhost',
httpPort: 9000,
},
},
log: {
level: 'info' as const,
pretty: true,
},
queue: {
redis: { host: 'localhost', port: 6379 },
},
};
const manager = new ConfigManager({
loaders: [new MockLoader(mockConfig)],
environment: env as any,
});
manager.initialize(baseAppSchema);
// Test the manager's environment detection
expect(manager.getEnvironment()).toBe(env);
expect(manager.get().environment).toBe(env);
}
});
});