196 lines
No EOL
5.6 KiB
TypeScript
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);
|
|
});
|
|
});
|
|
}); |