libs fully refactored
This commit is contained in:
parent
63baeaec70
commit
1b34da9a69
10 changed files with 181 additions and 21 deletions
|
|
@ -30,11 +30,38 @@ export class ServiceContainer implements ServiceResolver {
|
|||
this.logger.debug('Service registered', { name: registration.name, singleton: registration.singleton });
|
||||
}
|
||||
|
||||
resolve<T>(name: string, options?: any): T {
|
||||
const instance = this.resolveAsync<T>(name, options);
|
||||
resolve<T>(name: string, _options?: any): T {
|
||||
// Check scoped instances first
|
||||
if (this.scopedInstances.has(name)) {
|
||||
return this.scopedInstances.get(name);
|
||||
}
|
||||
|
||||
// Check singleton instances
|
||||
if (this.instances.has(name)) {
|
||||
return this.instances.get(name);
|
||||
}
|
||||
|
||||
// Get registration from this container or parent
|
||||
const registration = this.getRegistration(name);
|
||||
if (!registration) {
|
||||
throw new Error(`Service ${name} not registered`);
|
||||
}
|
||||
|
||||
// Create instance synchronously
|
||||
const instance = registration.factory();
|
||||
|
||||
// Check if factory returned a promise
|
||||
if (instance instanceof Promise) {
|
||||
throw new Error(`Service ${name} is async. Use resolveAsync() instead.`);
|
||||
}
|
||||
|
||||
// Store based on singleton flag
|
||||
if (registration.singleton) {
|
||||
this.instances.set(name, instance);
|
||||
} else {
|
||||
this.scopedInstances.set(name, instance);
|
||||
}
|
||||
|
||||
return instance as T;
|
||||
}
|
||||
|
||||
147
libs/core/di/test/di.test.ts
Normal file
147
libs/core/di/test/di.test.ts
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
/**
|
||||
* Test DI library functionality
|
||||
*/
|
||||
import { test, expect, describe } from 'bun:test';
|
||||
import { ServiceContainer, ConnectionFactory, OperationContext, PoolSizeCalculator } 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('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');
|
||||
});
|
||||
});
|
||||
|
|
@ -1,15 +1,15 @@
|
|||
{
|
||||
"extends": "../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "./",
|
||||
"rootDir": "./src",
|
||||
"outDir": "./dist",
|
||||
"composite": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"types": ["node", "bun-types"]
|
||||
},
|
||||
"include": ["./**/*.ts"],
|
||||
"exclude": ["node_modules", "dist"],
|
||||
"include": ["src/**/*.ts"],
|
||||
"exclude": ["node_modules", "dist", "test"],
|
||||
"references": [
|
||||
{ "path": "../config" },
|
||||
{ "path": "../logger" }
|
||||
|
|
|
|||
|
|
@ -13,19 +13,8 @@
|
|||
"@stock-bot/config": "workspace:*",
|
||||
"@stock-bot/logger": "workspace:*",
|
||||
"@stock-bot/cache": "workspace:*",
|
||||
"@stock-bot/postgres": "workspace:*",
|
||||
"@stock-bot/mongodb": "workspace:*",
|
||||
"@stock-bot/di": "workspace:*",
|
||||
"@stock-bot/types": "workspace:*",
|
||||
"@stock-bot/http": "workspace:*",
|
||||
"cheerio": "^1.0.0",
|
||||
"axios": "^1.7.7",
|
||||
"axios-rate-limit": "^1.4.0",
|
||||
"axios-retry": "^4.4.1",
|
||||
"socks-proxy-agent": "^8.0.2",
|
||||
"p-limit": "^6.1.0",
|
||||
"zod": "^3.22.4",
|
||||
"date-fns": "^2.30.0"
|
||||
"@stock-bot/http": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.11.0",
|
||||
|
|
|
|||
|
|
@ -14,9 +14,6 @@
|
|||
{ "path": "../data/cache" },
|
||||
{ "path": "../core/config" },
|
||||
{ "path": "../core/logger" },
|
||||
{ "path": "../services/http" },
|
||||
{ "path": "../core/di" },
|
||||
{ "path": "../data/mongodb" },
|
||||
{ "path": "../data/postgres" }
|
||||
{ "path": "../services/http" }
|
||||
]
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue