stock-bot/libs/core/handlers/test/base-handler-config.test.ts

215 lines
No EOL
7.2 KiB
TypeScript

import { describe, it, expect, beforeEach, mock } from 'bun:test';
import { BaseHandler } from '../src/base/BaseHandler';
import type { IServiceContainer, ExecutionContext } from '@stock-bot/types';
// Test handler with metadata
class ConfigTestHandler extends BaseHandler {
static __handlerName = 'config-test';
static __operations = [
{ name: 'process', method: 'processData' },
{ name: 'validate', method: 'validateData' },
];
static __schedules = [
{
operation: 'processData',
cronPattern: '0 * * * *',
priority: 5,
immediately: false,
description: 'Hourly processing',
payload: { type: 'scheduled' },
batch: { size: 100 },
},
];
static __description = 'Test handler for configuration';
async processData(input: any, context: ExecutionContext) {
return { processed: true, input };
}
async validateData(input: any, context: ExecutionContext) {
return { valid: true, input };
}
}
// Handler without metadata
class NoMetadataHandler extends BaseHandler {}
describe('BaseHandler Configuration', () => {
let mockServices: IServiceContainer;
beforeEach(() => {
mockServices = {
cache: null,
globalCache: null,
queueManager: null,
proxy: null,
browser: null,
mongodb: null,
postgres: null,
questdb: null,
} as any;
});
describe('createHandlerConfig', () => {
it('should create handler config from metadata', () => {
const handler = new ConfigTestHandler(mockServices);
const config = handler.createHandlerConfig();
expect(config.name).toBe('config-test');
expect(Object.keys(config.operations)).toEqual(['process', 'validate']);
expect(config.scheduledJobs).toHaveLength(1);
});
it('should create job handlers for operations', () => {
const handler = new ConfigTestHandler(mockServices);
const config = handler.createHandlerConfig();
expect(typeof config.operations.process).toBe('function');
expect(typeof config.operations.validate).toBe('function');
});
it('should include scheduled job details', () => {
const handler = new ConfigTestHandler(mockServices);
const config = handler.createHandlerConfig();
const scheduledJob = config.scheduledJobs[0];
expect(scheduledJob.type).toBe('config-test-processData');
expect(scheduledJob.operation).toBe('process');
expect(scheduledJob.cronPattern).toBe('0 * * * *');
expect(scheduledJob.priority).toBe(5);
expect(scheduledJob.immediately).toBe(false);
expect(scheduledJob.description).toBe('Hourly processing');
expect(scheduledJob.payload).toEqual({ type: 'scheduled' });
expect(scheduledJob.batch).toEqual({ size: 100 });
});
it('should execute operations through job handlers', async () => {
const handler = new ConfigTestHandler(mockServices);
const config = handler.createHandlerConfig();
// Mock the job execution
const processJob = config.operations.process;
const result = await processJob({ data: 'test' }, {} as any);
expect(result).toEqual({ processed: true, input: { data: 'test' } });
});
it('should throw error when no metadata found', () => {
const handler = new NoMetadataHandler(mockServices);
expect(() => handler.createHandlerConfig()).toThrow('Handler metadata not found');
});
it('should handle schedule without matching operation', () => {
class ScheduleOnlyHandler extends BaseHandler {
static __handlerName = 'schedule-only';
static __operations = [];
static __schedules = [
{
operation: 'nonExistentMethod',
cronPattern: '* * * * *',
},
];
}
const handler = new ScheduleOnlyHandler(mockServices);
const config = handler.createHandlerConfig();
expect(config.operations).toEqual({});
expect(config.scheduledJobs).toHaveLength(1);
expect(config.scheduledJobs[0].operation).toBe('nonExistentMethod');
});
it('should handle empty schedules array', () => {
class NoScheduleHandler extends BaseHandler {
static __handlerName = 'no-schedule';
static __operations = [{ name: 'test', method: 'testMethod' }];
static __schedules = [];
testMethod() {}
}
const handler = new NoScheduleHandler(mockServices);
const config = handler.createHandlerConfig();
expect(config.scheduledJobs).toEqual([]);
expect(config.operations).toHaveProperty('test');
});
it('should create execution context with proper metadata', async () => {
const handler = new ConfigTestHandler(mockServices);
const config = handler.createHandlerConfig();
// Spy on execute method
const executeSpy = mock();
handler.execute = executeSpy;
executeSpy.mockResolvedValue({ result: 'test' });
// Execute through job handler
await config.operations.process({ input: 'data' }, {} as any);
expect(executeSpy).toHaveBeenCalledWith(
'process',
{ input: 'data' },
expect.objectContaining({
type: 'queue',
metadata: expect.objectContaining({
source: 'queue',
timestamp: expect.any(Number),
}),
})
);
});
});
describe('extractMetadata', () => {
it('should extract complete metadata', () => {
const metadata = ConfigTestHandler.extractMetadata();
expect(metadata).not.toBeNull();
expect(metadata?.name).toBe('config-test');
expect(metadata?.operations).toEqual(['process', 'validate']);
expect(metadata?.description).toBe('Test handler for configuration');
expect(metadata?.scheduledJobs).toHaveLength(1);
});
it('should return null for handler without metadata', () => {
const metadata = NoMetadataHandler.extractMetadata();
expect(metadata).toBeNull();
});
it('should handle missing optional fields', () => {
class MinimalHandler extends BaseHandler {
static __handlerName = 'minimal';
static __operations = [];
}
const metadata = MinimalHandler.extractMetadata();
expect(metadata).not.toBeNull();
expect(metadata?.name).toBe('minimal');
expect(metadata?.operations).toEqual([]);
expect(metadata?.scheduledJobs).toEqual([]);
expect(metadata?.description).toBeUndefined();
});
it('should map schedule operations correctly', () => {
class MappedScheduleHandler extends BaseHandler {
static __handlerName = 'mapped';
static __operations = [
{ name: 'op1', method: 'method1' },
{ name: 'op2', method: 'method2' },
];
static __schedules = [
{ operation: 'method1', cronPattern: '* * * * *' },
{ operation: 'method2', cronPattern: '0 * * * *' },
];
}
const metadata = MappedScheduleHandler.extractMetadata();
expect(metadata?.scheduledJobs[0].operation).toBe('op1');
expect(metadata?.scheduledJobs[1].operation).toBe('op2');
});
});
});