/** * 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 { info = () => {}; debug = () => {}; warn = () => {}; error = () => {}; } class MockMongoDB implements Partial { 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); }); }); });