stock-bot/apps/stock/data-ingestion/test/shared/operation-manager/operation-manager.test.ts

196 lines
No EOL
5.6 KiB
TypeScript

/**
* Tests for the operation management system
*/
import { describe, it, expect, beforeEach } from 'bun:test';
import type { Logger, MongoDBClient } from '@stock-bot/types';
import {
BaseOperationProvider,
OperationRegistry,
OperationTracker,
type OperationConfig,
type ProviderConfig
} from '../../../src/shared/operation-manager';
// Mock implementations
class MockLogger implements Partial<Logger> {
info = () => {};
debug = () => {};
warn = () => {};
error = () => {};
}
class MockMongoDB implements Partial<MongoDBClient> {
collection = () => ({
createIndex: async () => ({}),
countDocuments: async () => 100,
bulkWrite: async () => ({ modifiedCount: 1 }),
aggregate: () => ({
toArray: async () => []
})
} as any);
updateOne = async () => ({});
find = async () => [];
findOne = async () => null;
}
// Test provider implementation
class TestProvider extends BaseOperationProvider {
getProviderConfig(): ProviderConfig {
return {
name: 'test',
collectionName: 'testSymbols',
symbolField: 'symbol'
};
}
getOperations(): OperationConfig[] {
return [
{
name: 'test_operation',
type: 'standard',
defaultStaleHours: 24
},
{
name: 'test_crawl',
type: 'crawl',
defaultStaleHours: 48,
requiresFinishedFlag: true
},
{
name: 'test_intraday_crawl',
type: 'intraday_crawl',
defaultStaleHours: 1,
requiresFinishedFlag: true
}
];
}
}
describe('Operation Management System', () => {
let logger: Logger;
let mongodb: MongoDBClient;
let registry: OperationRegistry;
let provider: TestProvider;
beforeEach(() => {
logger = new MockLogger() as Logger;
mongodb = new MockMongoDB() as MongoDBClient;
registry = new OperationRegistry({ mongodb, logger });
provider = new TestProvider({ mongodb, logger });
});
describe('BaseOperationProvider', () => {
it('should get provider config', () => {
const config = provider.getProviderConfig();
expect(config.name).toBe('test');
expect(config.collectionName).toBe('testSymbols');
expect(config.symbolField).toBe('symbol');
});
it('should get operations', () => {
const operations = provider.getOperations();
expect(operations).toHaveLength(3);
expect(operations[0].name).toBe('test_operation');
expect(operations[1].type).toBe('crawl');
expect(operations[2].type).toBe('intraday_crawl');
});
it('should get operation by name', () => {
const op = provider.getOperation('test_operation');
expect(op).toBeDefined();
expect(op?.name).toBe('test_operation');
});
it('should validate operation exists', () => {
expect(() => provider.validateOperation('test_operation')).not.toThrow();
expect(() => provider.validateOperation('invalid')).toThrow();
});
it('should get default stale hours', () => {
expect(provider.getDefaultStaleHours('test_operation')).toBe(24);
expect(provider.getDefaultStaleHours('test_crawl')).toBe(48);
expect(provider.getDefaultStaleHours('unknown')).toBe(24); // default
});
});
describe('OperationTracker', () => {
it('should initialize tracker', async () => {
const tracker = new OperationTracker({ mongodb, logger }, provider);
await tracker.initialize();
// Should create indexes without error
});
it('should update symbol operation', async () => {
const tracker = new OperationTracker({ mongodb, logger }, provider);
await tracker.initialize();
await tracker.updateSymbolOperation('TEST', 'test_operation', {
status: 'success',
recordCount: 100,
lastRecordDate: new Date()
});
// Should update without error
});
});
describe('OperationRegistry', () => {
it('should register provider', async () => {
await registry.registerProvider(provider);
expect(registry.hasProvider('test')).toBe(true);
});
it('should get provider', async () => {
await registry.registerProvider(provider);
const retrieved = registry.getProvider('test');
expect(retrieved).toBe(provider);
});
it('should throw for unknown provider', () => {
expect(() => registry.getProvider('unknown')).toThrow();
});
it('should get provider operations', async () => {
await registry.registerProvider(provider);
const operations = registry.getProviderOperations('test');
expect(operations).toHaveLength(3);
});
it('should update operation through registry', async () => {
await registry.registerProvider(provider);
await registry.updateOperation('test', 'TEST', 'test_operation', {
status: 'success',
recordCount: 50
});
// Should update without error
});
it('should find operations by type', async () => {
await registry.registerProvider(provider);
const crawlOps = registry.findOperationsByType('crawl');
expect(crawlOps).toHaveLength(1);
expect(crawlOps[0].provider).toBe('test');
expect(crawlOps[0].operation.name).toBe('test_crawl');
});
});
describe('Integration', () => {
it('should work end-to-end', async () => {
// Register provider
await registry.registerProvider(provider);
// Update operation
await registry.updateOperation('test', 'SYMBOL1', 'test_operation', {
status: 'success',
recordCount: 100
});
// Get stats (with mocked data)
const stats = await registry.getOperationStats('test', 'test_operation');
expect(stats.totalSymbols).toBe(100);
});
});
});