removed some old code
This commit is contained in:
parent
bd26ecf3bc
commit
c5f6b37022
11 changed files with 1 additions and 767 deletions
|
|
@ -1,47 +0,0 @@
|
|||
import type { AwilixContainer } from 'awilix';
|
||||
import { NamespacedCache, type CacheProvider } from '@stock-bot/cache';
|
||||
import type { ServiceDefinitions } from '../container/types';
|
||||
|
||||
export class CacheFactory {
|
||||
static createNamespacedCache(baseCache: CacheProvider, namespace: string): NamespacedCache {
|
||||
return new NamespacedCache(baseCache, namespace);
|
||||
}
|
||||
|
||||
static createCacheForService(
|
||||
container: AwilixContainer<ServiceDefinitions>,
|
||||
serviceName: string
|
||||
): CacheProvider | null {
|
||||
const baseCache = container.cradle.cache;
|
||||
if (!baseCache) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.createNamespacedCache(baseCache, serviceName);
|
||||
}
|
||||
|
||||
static createCacheForHandler(
|
||||
container: AwilixContainer<ServiceDefinitions>,
|
||||
handlerName: string
|
||||
): CacheProvider | null {
|
||||
const baseCache = container.cradle.cache;
|
||||
if (!baseCache) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.createNamespacedCache(baseCache, `handler:${handlerName}`);
|
||||
}
|
||||
|
||||
static createCacheWithPrefix(
|
||||
container: AwilixContainer<ServiceDefinitions>,
|
||||
prefix: string
|
||||
): CacheProvider | null {
|
||||
const baseCache = container.cradle.cache;
|
||||
if (!baseCache) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Remove 'cache:' prefix if already included
|
||||
const cleanPrefix = prefix.replace(/^cache:/, '');
|
||||
return this.createNamespacedCache(baseCache, cleanPrefix);
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +1 @@
|
|||
export { CacheFactory } from './cache.factory';
|
||||
// This directory is reserved for future factory implementations
|
||||
|
|
@ -18,5 +18,3 @@ export {
|
|||
queueConfigSchema
|
||||
} from './config/schemas';
|
||||
|
||||
// Export factories
|
||||
export { CacheFactory } from './factories';
|
||||
|
|
|
|||
|
|
@ -1,95 +0,0 @@
|
|||
import { asValue, createContainer } from 'awilix';
|
||||
import type { AwilixContainer } from 'awilix';
|
||||
import { describe, expect, it, mock } from 'bun:test';
|
||||
import type { CacheProvider } from '@stock-bot/cache';
|
||||
import type { ServiceDefinitions } from '../src/container/types';
|
||||
import { CacheFactory } from '../src/factories';
|
||||
|
||||
describe('DI Factories', () => {
|
||||
describe('CacheFactory', () => {
|
||||
const mockCache: CacheProvider = {
|
||||
get: mock(async () => null),
|
||||
set: mock(async () => {}),
|
||||
del: mock(async () => {}),
|
||||
clear: mock(async () => {}),
|
||||
has: mock(async () => false),
|
||||
keys: mock(async () => []),
|
||||
ttl: mock(async () => -1),
|
||||
type: 'memory',
|
||||
};
|
||||
|
||||
const createMockContainer = (
|
||||
cache: CacheProvider | null = mockCache
|
||||
): AwilixContainer<ServiceDefinitions> => {
|
||||
const container = createContainer<ServiceDefinitions>();
|
||||
container.register({
|
||||
cache: asValue(cache),
|
||||
});
|
||||
return container;
|
||||
};
|
||||
|
||||
it('should be exported', () => {
|
||||
expect(CacheFactory).toBeDefined();
|
||||
});
|
||||
|
||||
it('should create namespaced cache', () => {
|
||||
const namespacedCache = CacheFactory.createNamespacedCache(mockCache, 'test-namespace');
|
||||
|
||||
expect(namespacedCache).toBeDefined();
|
||||
expect(namespacedCache).toBeInstanceOf(Object);
|
||||
// NamespacedCache wraps the base cache but doesn't expose type property
|
||||
});
|
||||
|
||||
it('should create cache for service', () => {
|
||||
const container = createMockContainer();
|
||||
|
||||
const serviceCache = CacheFactory.createCacheForService(container, 'test-service');
|
||||
|
||||
expect(serviceCache).toBeDefined();
|
||||
expect(serviceCache).not.toBe(mockCache); // Should be a new namespaced instance
|
||||
});
|
||||
|
||||
it('should return null when no base cache available', () => {
|
||||
const container = createMockContainer(null);
|
||||
|
||||
const serviceCache = CacheFactory.createCacheForService(container, 'test-service');
|
||||
|
||||
expect(serviceCache).toBeNull();
|
||||
});
|
||||
|
||||
it('should create cache for handler with prefix', () => {
|
||||
const container = createMockContainer();
|
||||
|
||||
const handlerCache = CacheFactory.createCacheForHandler(container, 'TestHandler');
|
||||
|
||||
expect(handlerCache).toBeDefined();
|
||||
// The namespace should include 'handler:' prefix
|
||||
});
|
||||
|
||||
it('should create cache with custom prefix', () => {
|
||||
const container = createMockContainer();
|
||||
|
||||
const prefixedCache = CacheFactory.createCacheWithPrefix(container, 'custom-prefix');
|
||||
|
||||
expect(prefixedCache).toBeDefined();
|
||||
});
|
||||
|
||||
it('should clean duplicate cache: prefix', () => {
|
||||
const container = createMockContainer();
|
||||
|
||||
// Should handle prefix that already includes 'cache:'
|
||||
const prefixedCache = CacheFactory.createCacheWithPrefix(container, 'cache:custom-prefix');
|
||||
|
||||
expect(prefixedCache).toBeDefined();
|
||||
// Internally it should strip the duplicate 'cache:' prefix
|
||||
});
|
||||
|
||||
it('should handle null cache in all factory methods', () => {
|
||||
const container = createMockContainer(null);
|
||||
|
||||
expect(CacheFactory.createCacheForService(container, 'service')).toBeNull();
|
||||
expect(CacheFactory.createCacheForHandler(container, 'handler')).toBeNull();
|
||||
expect(CacheFactory.createCacheWithPrefix(container, 'prefix')).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -28,9 +28,6 @@ describe('DI Package Exports', () => {
|
|||
expect(diExports.HandlerScanner).toBeDefined();
|
||||
});
|
||||
|
||||
it('should export factories', () => {
|
||||
expect(diExports.CacheFactory).toBeDefined();
|
||||
});
|
||||
|
||||
it('should export schemas', () => {
|
||||
expect(diExports.appConfigSchema).toBeDefined();
|
||||
|
|
|
|||
|
|
@ -8,4 +8,3 @@ export {
|
|||
Disabled,
|
||||
} from './decorators/decorators';
|
||||
export { createJobHandler } from './utils/create-job-handler';
|
||||
export { autoRegisterHandlers, createAutoHandlerRegistry } from './registry/auto-register';
|
||||
|
|
|
|||
|
|
@ -1,195 +0,0 @@
|
|||
/**
|
||||
* Auto-registration utilities for handlers
|
||||
* Automatically discovers and registers handlers based on file patterns
|
||||
*/
|
||||
|
||||
import { readdirSync, statSync } from 'fs';
|
||||
import { join, relative } from 'path';
|
||||
import { getLogger } from '@stock-bot/logger';
|
||||
import type { IServiceContainer } from '@stock-bot/types';
|
||||
import { BaseHandler } from '../base/BaseHandler';
|
||||
|
||||
const logger = getLogger('handler-auto-register');
|
||||
|
||||
/**
|
||||
* Recursively find all handler files in a directory
|
||||
*/
|
||||
function findHandlerFiles(dir: string, pattern = '.handler.'): string[] {
|
||||
const files: string[] = [];
|
||||
|
||||
function scan(currentDir: string) {
|
||||
try {
|
||||
const entries = readdirSync(currentDir);
|
||||
|
||||
for (const entry of entries) {
|
||||
const fullPath = join(currentDir, entry);
|
||||
const stat = statSync(fullPath);
|
||||
|
||||
if (stat.isDirectory() && !entry.startsWith('.') && entry !== 'node_modules') {
|
||||
scan(fullPath);
|
||||
} else if (stat.isFile() && entry.includes(pattern) && entry.endsWith('.ts')) {
|
||||
files.push(fullPath);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// Directory doesn't exist or can't be read - that's okay
|
||||
logger.debug(`Cannot read directory ${currentDir}:`, { error });
|
||||
}
|
||||
}
|
||||
|
||||
scan(dir);
|
||||
return files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract handler classes from a module
|
||||
*/
|
||||
function extractHandlerClasses(
|
||||
module: any
|
||||
): Array<new (services: IServiceContainer) => BaseHandler> {
|
||||
const handlers: Array<new (services: IServiceContainer) => BaseHandler> = [];
|
||||
|
||||
for (const key of Object.keys(module)) {
|
||||
const exported = module[key];
|
||||
|
||||
// Check if it's a class that extends BaseHandler
|
||||
if (
|
||||
typeof exported === 'function' &&
|
||||
exported.prototype &&
|
||||
exported.prototype instanceof BaseHandler
|
||||
) {
|
||||
handlers.push(exported);
|
||||
}
|
||||
}
|
||||
|
||||
return handlers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto-register all handlers in a directory
|
||||
* @param directory The directory to scan for handlers
|
||||
* @param services The service container to inject into handlers
|
||||
* @param options Configuration options
|
||||
*/
|
||||
export async function autoRegisterHandlers(
|
||||
directory: string,
|
||||
services: IServiceContainer,
|
||||
options: {
|
||||
pattern?: string;
|
||||
exclude?: string[];
|
||||
dryRun?: boolean;
|
||||
serviceName?: string;
|
||||
} = {}
|
||||
): Promise<{ registered: string[]; failed: string[] }> {
|
||||
const { pattern = '.handler.', exclude = [], dryRun = false, serviceName } = options;
|
||||
const registered: string[] = [];
|
||||
const failed: string[] = [];
|
||||
|
||||
try {
|
||||
logger.info('Starting auto-registration of handlers', { directory, pattern });
|
||||
|
||||
// Find all handler files
|
||||
const handlerFiles = findHandlerFiles(directory, pattern);
|
||||
logger.debug(`Found ${handlerFiles.length} handler files`, { files: handlerFiles });
|
||||
|
||||
// Process each handler file
|
||||
for (const file of handlerFiles) {
|
||||
const relativePath = relative(directory, file);
|
||||
|
||||
// Skip excluded files
|
||||
if (exclude.some(ex => relativePath.includes(ex))) {
|
||||
logger.debug(`Skipping excluded file: ${relativePath}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
// Import the module
|
||||
const module = await import(file);
|
||||
const handlerClasses = extractHandlerClasses(module);
|
||||
|
||||
if (handlerClasses.length === 0) {
|
||||
logger.warn(`No handler classes found in ${relativePath}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Register each handler class
|
||||
for (const HandlerClass of handlerClasses) {
|
||||
const handlerName = HandlerClass.name;
|
||||
|
||||
// Check if handler is disabled
|
||||
if ((HandlerClass as any).__disabled) {
|
||||
logger.info(`Skipping disabled handler: ${handlerName} from ${relativePath}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (dryRun) {
|
||||
logger.info(`[DRY RUN] Would register handler: ${handlerName} from ${relativePath}`);
|
||||
registered.push(handlerName);
|
||||
} else {
|
||||
logger.info(`Registering handler: ${handlerName} from ${relativePath}`);
|
||||
|
||||
// Create instance - handlers now auto-register via decorators
|
||||
new HandlerClass(services);
|
||||
|
||||
registered.push(handlerName);
|
||||
logger.info(`Successfully registered handler: ${handlerName}`, {
|
||||
service: serviceName,
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`Failed to process handler file: ${relativePath}`, { error });
|
||||
failed.push(relativePath);
|
||||
}
|
||||
}
|
||||
|
||||
logger.info('Auto-registration complete', {
|
||||
totalFiles: handlerFiles.length,
|
||||
registered: registered.length,
|
||||
failed: failed.length,
|
||||
});
|
||||
|
||||
return { registered, failed };
|
||||
} catch (error) {
|
||||
logger.error('Auto-registration failed', { error });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a handler registry that auto-discovers handlers
|
||||
*/
|
||||
export function createAutoHandlerRegistry(services: IServiceContainer) {
|
||||
return {
|
||||
/**
|
||||
* Register all handlers from a directory
|
||||
*/
|
||||
async registerDirectory(
|
||||
directory: string,
|
||||
options?: Parameters<typeof autoRegisterHandlers>[2]
|
||||
) {
|
||||
return autoRegisterHandlers(directory, services, options);
|
||||
},
|
||||
|
||||
/**
|
||||
* Register handlers from multiple directories
|
||||
*/
|
||||
async registerDirectories(
|
||||
directories: string[],
|
||||
options?: Parameters<typeof autoRegisterHandlers>[2]
|
||||
) {
|
||||
const results = {
|
||||
registered: [] as string[],
|
||||
failed: [] as string[],
|
||||
};
|
||||
|
||||
for (const dir of directories) {
|
||||
const result = await autoRegisterHandlers(dir, services, options);
|
||||
results.registered.push(...result.registered);
|
||||
results.failed.push(...result.failed);
|
||||
}
|
||||
|
||||
return results;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
import { afterEach, beforeEach, describe, expect, it, mock } from 'bun:test';
|
||||
import type { IServiceContainer } from '@stock-bot/types';
|
||||
import { BaseHandler } from '../src/base/BaseHandler';
|
||||
import { autoRegisterHandlers, createAutoHandlerRegistry } from '../src/registry/auto-register';
|
||||
|
||||
describe('Auto Registration - Simple Tests', () => {
|
||||
describe('autoRegisterHandlers', () => {
|
||||
it('should return empty results for non-existent directory', async () => {
|
||||
const mockServices = {} as IServiceContainer;
|
||||
const result = await autoRegisterHandlers('./non-existent-directory', mockServices);
|
||||
|
||||
expect(result.registered).toEqual([]);
|
||||
expect(result.failed).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle directory with no handler files', async () => {
|
||||
const mockServices = {} as IServiceContainer;
|
||||
// Use the test directory itself which has no handler files
|
||||
const result = await autoRegisterHandlers('./test', mockServices);
|
||||
|
||||
expect(result.registered).toEqual([]);
|
||||
expect(result.failed).toEqual([]);
|
||||
});
|
||||
|
||||
it('should support dry run mode', async () => {
|
||||
const mockServices = {} as IServiceContainer;
|
||||
const result = await autoRegisterHandlers('./non-existent', mockServices, { dryRun: true });
|
||||
|
||||
expect(result.registered).toEqual([]);
|
||||
expect(result.failed).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle excluded patterns', async () => {
|
||||
const mockServices = {} as IServiceContainer;
|
||||
const result = await autoRegisterHandlers('./test', mockServices, {
|
||||
exclude: ['test'],
|
||||
});
|
||||
|
||||
expect(result.registered).toEqual([]);
|
||||
expect(result.failed).toEqual([]);
|
||||
});
|
||||
|
||||
it('should accept custom pattern', async () => {
|
||||
const mockServices = {} as IServiceContainer;
|
||||
const result = await autoRegisterHandlers('./test', mockServices, {
|
||||
pattern: '.custom.',
|
||||
});
|
||||
|
||||
expect(result.registered).toEqual([]);
|
||||
expect(result.failed).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('createAutoHandlerRegistry', () => {
|
||||
it('should create registry with registerDirectory method', () => {
|
||||
const mockServices = {} as IServiceContainer;
|
||||
const registry = createAutoHandlerRegistry(mockServices);
|
||||
|
||||
expect(registry).toHaveProperty('registerDirectory');
|
||||
expect(registry).toHaveProperty('registerDirectories');
|
||||
expect(typeof registry.registerDirectory).toBe('function');
|
||||
expect(typeof registry.registerDirectories).toBe('function');
|
||||
});
|
||||
|
||||
it('should register from multiple directories', async () => {
|
||||
const mockServices = {} as IServiceContainer;
|
||||
const registry = createAutoHandlerRegistry(mockServices);
|
||||
|
||||
const result = await registry.registerDirectories(['./non-existent-1', './non-existent-2']);
|
||||
|
||||
expect(result.registered).toEqual([]);
|
||||
expect(result.failed).toEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,204 +0,0 @@
|
|||
import { describe, expect, it, mock } from 'bun:test';
|
||||
import { BaseHandler } from '../src/base/BaseHandler';
|
||||
|
||||
// Test the internal functions by mocking module imports
|
||||
describe('Auto Registration Unit Tests', () => {
|
||||
describe('extractHandlerClasses', () => {
|
||||
it('should extract handler classes from module', () => {
|
||||
// Test handler class
|
||||
class TestHandler extends BaseHandler {}
|
||||
class AnotherHandler extends BaseHandler {}
|
||||
class NotAHandler {}
|
||||
|
||||
const module = {
|
||||
TestHandler,
|
||||
AnotherHandler,
|
||||
NotAHandler,
|
||||
someFunction: () => {},
|
||||
someVariable: 42,
|
||||
};
|
||||
|
||||
// Access the private function through module internals
|
||||
const autoRegister = require('../src/registry/auto-register');
|
||||
|
||||
// Mock the extractHandlerClasses function behavior
|
||||
const handlers = [];
|
||||
for (const key of Object.keys(module)) {
|
||||
const exported = module[key];
|
||||
if (
|
||||
typeof exported === 'function' &&
|
||||
exported.prototype &&
|
||||
exported.prototype instanceof BaseHandler
|
||||
) {
|
||||
handlers.push(exported);
|
||||
}
|
||||
}
|
||||
|
||||
expect(handlers).toHaveLength(2);
|
||||
expect(handlers).toContain(TestHandler);
|
||||
expect(handlers).toContain(AnotherHandler);
|
||||
expect(handlers).not.toContain(NotAHandler);
|
||||
});
|
||||
});
|
||||
|
||||
describe('findHandlerFiles', () => {
|
||||
it('should filter files by pattern', () => {
|
||||
const files = [
|
||||
'test.handler.ts',
|
||||
'test.service.ts',
|
||||
'another.handler.ts',
|
||||
'test.handler.js',
|
||||
'.hidden.handler.ts',
|
||||
];
|
||||
|
||||
const pattern = '.handler.';
|
||||
const filtered = files.filter(
|
||||
file => file.includes(pattern) && file.endsWith('.ts') && !file.startsWith('.')
|
||||
);
|
||||
|
||||
expect(filtered).toEqual(['test.handler.ts', 'another.handler.ts']);
|
||||
});
|
||||
|
||||
it('should handle different patterns', () => {
|
||||
const files = ['test.handler.ts', 'test.custom.ts', 'another.custom.ts'];
|
||||
|
||||
const customPattern = '.custom.';
|
||||
const filtered = files.filter(file => file.includes(customPattern) && file.endsWith('.ts'));
|
||||
|
||||
expect(filtered).toEqual(['test.custom.ts', 'another.custom.ts']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Handler Registration Logic', () => {
|
||||
it('should skip disabled handlers', () => {
|
||||
class DisabledHandler extends BaseHandler {
|
||||
static __disabled = true;
|
||||
}
|
||||
|
||||
class EnabledHandler extends BaseHandler {}
|
||||
|
||||
const handlers = [DisabledHandler, EnabledHandler];
|
||||
const registered = handlers.filter(h => !(h as any).__disabled);
|
||||
|
||||
expect(registered).toHaveLength(1);
|
||||
expect(registered).toContain(EnabledHandler);
|
||||
expect(registered).not.toContain(DisabledHandler);
|
||||
});
|
||||
|
||||
it('should handle handler with auto-registration flag', () => {
|
||||
class AutoRegisterHandler extends BaseHandler {
|
||||
static __handlerName = 'auto-handler';
|
||||
static __needsAutoRegistration = true;
|
||||
}
|
||||
|
||||
expect((AutoRegisterHandler as any).__needsAutoRegistration).toBe(true);
|
||||
expect((AutoRegisterHandler as any).__handlerName).toBe('auto-handler');
|
||||
});
|
||||
|
||||
it('should create handler instance with services', () => {
|
||||
const mockServices = {
|
||||
cache: null,
|
||||
globalCache: null,
|
||||
queueManager: null,
|
||||
proxy: null,
|
||||
browser: null,
|
||||
mongodb: null,
|
||||
postgres: null,
|
||||
questdb: null,
|
||||
} as any;
|
||||
|
||||
class TestHandler extends BaseHandler {}
|
||||
|
||||
const instance = new TestHandler(mockServices);
|
||||
expect(instance).toBeInstanceOf(BaseHandler);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Error Handling', () => {
|
||||
it('should handle module import errors gracefully', () => {
|
||||
const errors = [];
|
||||
const modules = ['valid', 'error', 'another'];
|
||||
|
||||
for (const mod of modules) {
|
||||
try {
|
||||
if (mod === 'error') {
|
||||
throw new Error('Module not found');
|
||||
}
|
||||
// Process module
|
||||
} catch (error) {
|
||||
errors.push(mod);
|
||||
}
|
||||
}
|
||||
|
||||
expect(errors).toEqual(['error']);
|
||||
});
|
||||
|
||||
it('should handle filesystem errors', () => {
|
||||
let result;
|
||||
try {
|
||||
// Simulate filesystem error
|
||||
throw new Error('EACCES: permission denied');
|
||||
} catch (error) {
|
||||
// Should handle gracefully
|
||||
result = { registered: [], failed: [] };
|
||||
}
|
||||
|
||||
expect(result).toEqual({ registered: [], failed: [] });
|
||||
});
|
||||
});
|
||||
|
||||
describe('Options Handling', () => {
|
||||
it('should apply exclude patterns', () => {
|
||||
const files = ['test.handler.ts', 'excluded.handler.ts', 'another.handler.ts'];
|
||||
const exclude = ['excluded'];
|
||||
|
||||
const filtered = files.filter(file => !exclude.some(ex => file.includes(ex)));
|
||||
|
||||
expect(filtered).toEqual(['test.handler.ts', 'another.handler.ts']);
|
||||
});
|
||||
|
||||
it('should handle service name option', () => {
|
||||
const options = {
|
||||
pattern: '.handler.',
|
||||
exclude: [],
|
||||
dryRun: false,
|
||||
serviceName: 'test-service',
|
||||
};
|
||||
|
||||
expect(options.serviceName).toBe('test-service');
|
||||
});
|
||||
|
||||
it('should handle dry run mode', () => {
|
||||
const options = { dryRun: true };
|
||||
const actions = [];
|
||||
|
||||
if (options.dryRun) {
|
||||
actions.push('[DRY RUN] Would register handler');
|
||||
} else {
|
||||
actions.push('Registering handler');
|
||||
}
|
||||
|
||||
expect(actions).toEqual(['[DRY RUN] Would register handler']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Registry Methods', () => {
|
||||
it('should handle multiple directories', () => {
|
||||
const directories = ['./dir1', './dir2', './dir3'];
|
||||
const results = {
|
||||
registered: [] as string[],
|
||||
failed: [] as string[],
|
||||
};
|
||||
|
||||
for (const dir of directories) {
|
||||
// Simulate processing each directory
|
||||
results.registered.push(`${dir}-handler`);
|
||||
}
|
||||
|
||||
expect(results.registered).toHaveLength(3);
|
||||
expect(results.registered).toContain('./dir1-handler');
|
||||
expect(results.registered).toContain('./dir2-handler');
|
||||
expect(results.registered).toContain('./dir3-handler');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,138 +0,0 @@
|
|||
import { beforeEach, describe, expect, it, mock } from 'bun:test';
|
||||
import type { IServiceContainer } from '@stock-bot/types';
|
||||
import { BaseHandler } from '../src/base/BaseHandler';
|
||||
import { autoRegisterHandlers, createAutoHandlerRegistry } from '../src/registry/auto-register';
|
||||
|
||||
describe('Auto Registration', () => {
|
||||
describe('autoRegisterHandlers', () => {
|
||||
it('should auto-register handlers', async () => {
|
||||
const mockServices = {} as IServiceContainer;
|
||||
// Using a directory that doesn't exist - the function handles this gracefully
|
||||
const result = await autoRegisterHandlers('./non-existent', mockServices);
|
||||
|
||||
expect(result).toHaveProperty('registered');
|
||||
expect(result).toHaveProperty('failed');
|
||||
expect(result.registered).toEqual([]);
|
||||
expect(result.failed).toEqual([]);
|
||||
});
|
||||
|
||||
it('should use default options when not provided', async () => {
|
||||
const mockServices = {} as IServiceContainer;
|
||||
const result = await autoRegisterHandlers('./test', mockServices);
|
||||
|
||||
expect(result).toBeDefined();
|
||||
expect(result.registered).toBeInstanceOf(Array);
|
||||
expect(result.failed).toBeInstanceOf(Array);
|
||||
});
|
||||
|
||||
it('should handle directory not found gracefully', async () => {
|
||||
const mockServices = {} as IServiceContainer;
|
||||
|
||||
// Should not throw for non-existent directory
|
||||
const result = await autoRegisterHandlers('./non-existent-directory', mockServices);
|
||||
expect(result.registered).toEqual([]);
|
||||
expect(result.failed).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('createAutoHandlerRegistry', () => {
|
||||
it('should create a registry with registerDirectory method', () => {
|
||||
const mockServices = {} as IServiceContainer;
|
||||
const registry = createAutoHandlerRegistry(mockServices);
|
||||
|
||||
expect(registry).toHaveProperty('registerDirectory');
|
||||
expect(typeof registry.registerDirectory).toBe('function');
|
||||
});
|
||||
|
||||
it('should register from a directory', async () => {
|
||||
const mockServices = {} as IServiceContainer;
|
||||
const registry = createAutoHandlerRegistry(mockServices);
|
||||
|
||||
const result = await registry.registerDirectory('./non-existent-dir');
|
||||
expect(result).toHaveProperty('registered');
|
||||
expect(result).toHaveProperty('failed');
|
||||
});
|
||||
|
||||
it('should register from multiple directories', async () => {
|
||||
const mockServices = {} as IServiceContainer;
|
||||
const registry = createAutoHandlerRegistry(mockServices);
|
||||
|
||||
const result = await registry.registerDirectories(['./dir1', './dir2']);
|
||||
expect(result).toHaveProperty('registered');
|
||||
expect(result).toHaveProperty('failed');
|
||||
expect(result.registered).toBeInstanceOf(Array);
|
||||
expect(result.failed).toBeInstanceOf(Array);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Edge Cases', () => {
|
||||
it('should handle non-existent directories gracefully', async () => {
|
||||
const mockServices = {} as any;
|
||||
|
||||
// Should not throw, just return empty results
|
||||
const result = await autoRegisterHandlers('./definitely-does-not-exist-12345', mockServices);
|
||||
expect(result.registered).toEqual([]);
|
||||
expect(result.failed).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle empty options', async () => {
|
||||
const mockServices = {} as any;
|
||||
|
||||
// Should use default options
|
||||
const result = await autoRegisterHandlers('./test', mockServices, {});
|
||||
expect(result).toBeDefined();
|
||||
expect(result.registered).toBeInstanceOf(Array);
|
||||
expect(result.failed).toBeInstanceOf(Array);
|
||||
});
|
||||
|
||||
it('should support service name in options', async () => {
|
||||
const mockServices = {} as any;
|
||||
|
||||
const result = await autoRegisterHandlers('./test', mockServices, {
|
||||
serviceName: 'test-service',
|
||||
});
|
||||
|
||||
expect(result).toBeDefined();
|
||||
});
|
||||
|
||||
it('should handle dry run mode', async () => {
|
||||
const mockServices = {} as any;
|
||||
const result = await autoRegisterHandlers('./test', mockServices, { dryRun: true });
|
||||
|
||||
expect(result).toBeDefined();
|
||||
expect(result.registered).toBeInstanceOf(Array);
|
||||
expect(result.failed).toBeInstanceOf(Array);
|
||||
});
|
||||
|
||||
it('should handle excluded files', async () => {
|
||||
const mockServices = {} as any;
|
||||
const result = await autoRegisterHandlers('./test', mockServices, {
|
||||
exclude: ['test'],
|
||||
});
|
||||
|
||||
expect(result).toBeDefined();
|
||||
expect(result.registered).toBeInstanceOf(Array);
|
||||
expect(result.failed).toBeInstanceOf(Array);
|
||||
});
|
||||
|
||||
it('should handle custom pattern', async () => {
|
||||
const mockServices = {} as any;
|
||||
const result = await autoRegisterHandlers('./test', mockServices, { pattern: '.custom.' });
|
||||
|
||||
expect(result).toBeDefined();
|
||||
expect(result.registered).toBeInstanceOf(Array);
|
||||
expect(result.failed).toBeInstanceOf(Array);
|
||||
});
|
||||
|
||||
it('should handle errors gracefully', async () => {
|
||||
const mockServices = {} as any;
|
||||
|
||||
// Even with a protected directory, it should handle gracefully
|
||||
const result = await autoRegisterHandlers('./protected-dir', mockServices);
|
||||
|
||||
expect(result).toBeDefined();
|
||||
expect(result.registered).toBeInstanceOf(Array);
|
||||
expect(result.failed).toBeInstanceOf(Array);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -28,12 +28,6 @@ describe('Handlers Package Exports', () => {
|
|||
expect(typeof handlersExports.Disabled).toBe('function');
|
||||
});
|
||||
|
||||
it('should export auto-registration utilities', () => {
|
||||
expect(handlersExports.autoRegisterHandlers).toBeDefined();
|
||||
expect(handlersExports.createAutoHandlerRegistry).toBeDefined();
|
||||
expect(typeof handlersExports.autoRegisterHandlers).toBe('function');
|
||||
expect(typeof handlersExports.createAutoHandlerRegistry).toBe('function');
|
||||
});
|
||||
|
||||
it('should export types', () => {
|
||||
// Type tests - compile-time checks
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue