stock-bot/libs/core/di/test/di.test.ts
2025-06-25 08:29:53 -04:00

264 lines
No EOL
7.8 KiB
TypeScript

import { describe, it, expect, beforeEach, mock } from 'bun:test';
import { createContainer, InjectionMode, asClass, asFunction, asValue } from 'awilix';
import { ServiceContainerBuilder } from '../src/container/builder';
import { ServiceApplication } from '../src/service-application';
import { HandlerScanner } from '../src/scanner/handler-scanner';
import { OperationContext } from '../src/operation-context';
import { PoolSizeCalculator } from '../src/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
});
});
});