removed old tests, created new ones and format

This commit is contained in:
Boki 2025-06-25 07:46:59 -04:00
parent 7579afa3c3
commit b03231b849
57 changed files with 4092 additions and 5901 deletions

View file

@ -1,7 +1,7 @@
import { asClass, asFunction, createContainer, InjectionMode, type AwilixContainer } from 'awilix';
import type { BaseAppConfig as StockBotAppConfig, UnifiedAppConfig } from '@stock-bot/config';
import { toUnifiedConfig } from '@stock-bot/config';
import { HandlerRegistry } from '@stock-bot/handler-registry';
import { asClass, asFunction, createContainer, InjectionMode, type AwilixContainer } from 'awilix';
import { appConfigSchema, type AppConfig } from '../config/schemas';
import {
registerApplicationServices,

264
libs/core/di/src/di.test.ts Normal file
View file

@ -0,0 +1,264 @@
import { describe, it, expect, beforeEach, mock } from 'bun:test';
import { createContainer, InjectionMode, asClass, asFunction, asValue } from 'awilix';
import { ServiceContainerBuilder } from './container/builder';
import { ServiceApplication } from './service-application';
import { HandlerScanner } from './scanner/handler-scanner';
import { OperationContext } from './operation-context';
import { PoolSizeCalculator } from './pool-size-calculator';
describe('Dependency Injection', () => {
describe('ServiceContainerBuilder', () => {
let builder: ServiceContainerBuilder;
beforeEach(() => {
builder = new ServiceContainerBuilder();
});
it('should create container with default configuration', async () => {
const config = {
name: 'test-service',
version: '1.0.0',
service: {
name: 'test-service',
type: 'WORKER' as const,
serviceName: 'test-service',
port: 3000,
},
log: {
level: 'info',
format: 'json',
},
};
builder.withConfig(config);
builder.skipInitialization(); // Skip initialization for testing
const container = await builder.build();
expect(container).toBeDefined();
});
it('should configure services', async () => {
const config = {
name: 'test-service',
version: '1.0.0',
service: {
name: 'test-service',
type: 'WORKER' as const,
serviceName: 'test-service',
port: 3000,
},
log: {
level: 'info',
format: 'json',
},
};
builder
.withConfig(config)
.withOptions({
enableCache: true,
enableQueue: false,
})
.skipInitialization();
const container = await builder.build();
expect(container).toBeDefined();
});
});
describe('Basic Container Operations', () => {
it('should register and resolve values', () => {
const container = createContainer({
injectionMode: InjectionMode.PROXY,
});
container.register({
testValue: asValue('test'),
});
expect(container.resolve('testValue')).toBe('test');
});
it('should register and resolve classes', () => {
class TestClass {
getValue() {
return 'test';
}
}
const container = createContainer({
injectionMode: InjectionMode.PROXY,
});
container.register({
testClass: asClass(TestClass),
});
const instance = container.resolve('testClass');
expect(instance).toBeInstanceOf(TestClass);
expect(instance.getValue()).toBe('test');
});
it('should handle dependencies', () => {
const container = createContainer({
injectionMode: InjectionMode.PROXY,
});
// Test with scoped container
container.register({
config: asValue({ host: 'localhost', port: 5432 }),
connection: asFunction(() => {
const config = container.resolve('config');
return `postgresql://${config.host}:${config.port}/mydb`;
}).scoped(),
});
const connection = container.resolve('connection');
expect(connection).toBe('postgresql://localhost:5432/mydb');
});
});
describe('OperationContext', () => {
it('should create operation context', () => {
const context = new OperationContext({
handlerName: 'test-handler',
operationName: 'test-op',
});
expect(context.traceId).toBeDefined();
expect(context.logger).toBeDefined();
expect(context.metadata).toEqual({});
});
it('should include metadata', () => {
const metadata = { userId: '123', source: 'api' };
const context = new OperationContext({
handlerName: 'test-handler',
operationName: 'test-op',
metadata,
});
expect(context.metadata).toEqual(metadata);
});
it('should track execution time', async () => {
const context = new OperationContext({
handlerName: 'test-handler',
operationName: 'test-op',
});
await new Promise(resolve => setTimeout(resolve, 10));
const executionTime = context.getExecutionTime();
expect(executionTime).toBeGreaterThanOrEqual(10);
});
it('should create child context', () => {
const parentContext = new OperationContext({
handlerName: 'parent-handler',
operationName: 'parent-op',
metadata: { parentId: '123' },
});
const childContext = parentContext.createChild('child-op', { childId: '456' });
expect(childContext.traceId).toBe(parentContext.traceId);
expect(childContext.metadata).toEqual({ parentId: '123', childId: '456' });
});
});
describe('HandlerScanner', () => {
it('should create scanner instance', () => {
const mockRegistry = {
register: mock(() => {}),
getHandlers: mock(() => []),
};
const mockContainer = createContainer({
injectionMode: InjectionMode.PROXY,
});
const scanner = new HandlerScanner(mockRegistry as any, mockContainer);
expect(scanner).toBeDefined();
expect(scanner.scanHandlers).toBeDefined();
});
});
describe('ServiceApplication', () => {
it('should create service application', () => {
const mockConfig = {
name: 'test-service',
version: '1.0.0',
service: {
name: 'test-service',
type: 'WORKER' as const,
serviceName: 'test-service',
port: 3000,
},
log: {
level: 'info',
format: 'json',
},
};
const serviceConfig = {
serviceName: 'test-service',
};
const app = new ServiceApplication(mockConfig, serviceConfig);
expect(app).toBeDefined();
expect(app.start).toBeDefined();
expect(app.stop).toBeDefined();
});
});
describe('Pool Size Calculator', () => {
it('should calculate pool size for services', () => {
const recommendation = PoolSizeCalculator.calculate('web-api');
expect(recommendation.min).toBe(2);
expect(recommendation.max).toBe(10);
expect(recommendation.idle).toBe(2);
});
it('should calculate pool size for handlers', () => {
const recommendation = PoolSizeCalculator.calculate('data-ingestion', 'batch-import');
expect(recommendation.min).toBe(10);
expect(recommendation.max).toBe(100);
expect(recommendation.idle).toBe(20);
});
it('should use custom configuration', () => {
const recommendation = PoolSizeCalculator.calculate('custom', undefined, {
minConnections: 5,
maxConnections: 50,
});
expect(recommendation.min).toBe(5);
expect(recommendation.max).toBe(50);
expect(recommendation.idle).toBe(13); // (5+50)/4 = 13.75 -> 13
});
it('should fall back to defaults', () => {
const recommendation = PoolSizeCalculator.calculate('unknown-service');
expect(recommendation.min).toBe(2);
expect(recommendation.max).toBe(10);
expect(recommendation.idle).toBe(3);
});
it('should calculate optimal pool size', () => {
const size = PoolSizeCalculator.getOptimalPoolSize(
100, // 100 requests per second
50, // 50ms average query time
100 // 100ms target latency
);
expect(size).toBeGreaterThan(0);
expect(size).toBe(50); // max(100*0.05*1.2, 100*50/100, 2) = max(6, 50, 2) = 50
});
});
});

View file

@ -1,6 +1,6 @@
import { asClass, asFunction, asValue, type AwilixContainer } from 'awilix';
import { Browser } from '@stock-bot/browser';
import { ProxyManager } from '@stock-bot/proxy';
import { asClass, asFunction, asValue, type AwilixContainer } from 'awilix';
import type { AppConfig } from '../config/schemas';
import type { ServiceDefinitions } from '../container/types';

View file

@ -82,7 +82,9 @@ export class HandlerScanner {
* Check if an exported value is a handler
*/
private isHandler(exported: any): boolean {
if (typeof exported !== 'function') {return false;}
if (typeof exported !== 'function') {
return false;
}
// Check for handler metadata added by decorators
const hasHandlerName = !!(exported as any).__handlerName;

View file

@ -1,183 +0,0 @@
/**
* Test DI library functionality
*/
import { describe, expect, test } from 'bun:test';
import {
ConnectionFactory,
OperationContext,
PoolSizeCalculator,
ServiceContainer,
} from '../src/index';
describe('DI Library', () => {
test('ServiceContainer - sync resolution', () => {
const container = new ServiceContainer('test');
container.register({
name: 'testService',
factory: () => ({ value: 'test' }),
singleton: true,
});
const service = container.resolve<{ value: string }>('testService');
expect(service.value).toBe('test');
});
test('ServiceContainer - async resolution', async () => {
const container = new ServiceContainer('test');
container.register({
name: 'asyncService',
factory: async () => ({ value: 'async-test' }),
singleton: true,
});
const service = await container.resolveAsync<{ value: string }>('asyncService');
expect(service.value).toBe('async-test');
});
test('ServiceContainer - scoped container', () => {
const container = new ServiceContainer('test');
container.register({
name: 'testService',
factory: () => ({ value: 'test' }),
singleton: true,
});
const scopedContainer = container.createScope();
const service = scopedContainer.resolve<{ value: string }>('testService');
expect(service.value).toBe('test');
});
test('ServiceContainer - error on unregistered service', () => {
const container = new ServiceContainer('test');
expect(() => {
container.resolve('nonexistent');
}).toThrow('Service nonexistent not registered');
});
test('ServiceContainer - async service throws error on sync resolve', () => {
const container = new ServiceContainer('test');
container.register({
name: 'asyncService',
factory: async () => ({ value: 'async' }),
singleton: true,
});
expect(() => {
container.resolve('asyncService');
}).toThrow('Service asyncService is async. Use resolveAsync() instead.');
});
test('ServiceContainer - disposal', async () => {
const container = new ServiceContainer('test');
let disposed = false;
container.register({
name: 'disposableService',
factory: () => ({ value: 'test' }),
singleton: true,
dispose: async () => {
disposed = true;
},
});
// Create instance
container.resolve('disposableService');
// Dispose container
await container.dispose();
expect(disposed).toBe(true);
});
test('OperationContext - enhanced functionality', () => {
const container = new ServiceContainer('test');
const context = OperationContext.create('test-handler', 'test-operation', {
container,
metadata: { userId: '123' },
});
expect(context).toBeDefined();
expect(context.logger).toBeDefined();
expect(context.traceId).toBeDefined();
expect(context.metadata.userId).toBe('123');
expect(context.getExecutionTime()).toBeGreaterThanOrEqual(0);
});
test('OperationContext - service resolution', () => {
const container = new ServiceContainer('test');
container.register({
name: 'testService',
factory: () => ({ value: 'resolved' }),
singleton: true,
});
const context = OperationContext.create('test-handler', 'test-operation', {
container,
});
const service = context.resolve<{ value: string }>('testService');
expect(service.value).toBe('resolved');
});
test('ConnectionFactory - creation', () => {
const factory = new ConnectionFactory({
service: 'test',
environment: 'development',
});
expect(factory).toBeDefined();
expect(factory.listPools()).toEqual([]);
});
test('OperationContext - creation', () => {
const container = new ServiceContainer('test');
const context = OperationContext.create('test-handler', 'test-operation', {
container,
});
expect(context).toBeDefined();
expect(context.logger).toBeDefined();
});
test('OperationContext - child context', () => {
const context = OperationContext.create('test-handler', 'test-operation');
const child = context.createChild('child-operation');
expect(child).toBeDefined();
expect(child.logger).toBeDefined();
});
test('PoolSizeCalculator - service defaults', () => {
const poolSize = PoolSizeCalculator.calculate('data-ingestion');
expect(poolSize).toEqual({ min: 5, max: 50, idle: 10 });
});
test('PoolSizeCalculator - handler defaults', () => {
const poolSize = PoolSizeCalculator.calculate('unknown-service', 'batch-import');
expect(poolSize).toEqual({ min: 10, max: 100, idle: 20 });
});
test('PoolSizeCalculator - fallback defaults', () => {
const poolSize = PoolSizeCalculator.calculate('unknown-service', 'unknown-handler');
expect(poolSize).toEqual({ min: 2, max: 10, idle: 3 });
});
test('PoolSizeCalculator - custom config', () => {
const poolSize = PoolSizeCalculator.calculate('test-service', undefined, {
minConnections: 5,
maxConnections: 15,
});
expect(poolSize).toEqual({ min: 5, max: 15, idle: 5 });
});
test('PoolSizeCalculator - optimal size calculation', () => {
const optimalSize = PoolSizeCalculator.getOptimalPoolSize(10, 100, 50);
expect(optimalSize).toBeGreaterThan(0);
expect(typeof optimalSize).toBe('number');
});
});