UNPKG

@ahmedhegazee/nestjs-telescope

Version:

Advanced observability and monitoring solution for NestJS applications with ML-powered analytics, enterprise features, and production-ready scaling

360 lines 15.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const testing_1 = require("@nestjs/testing"); const core_1 = require("@nestjs/core"); const watcher_registry_service_1 = require("./watcher-registry.service"); const enhanced_entry_manager_service_1 = require("../core/services/enhanced-entry-manager.service"); class MockWatcher { constructor(name) { this.name = name; this._enabled = false; } get isEnabled() { return this._enabled; } enable() { this._enabled = true; } disable() { this._enabled = false; } async cleanup() { } async healthCheck() { return this._enabled; } } describe('WatcherRegistryService', () => { let service; let mockEntryManager; let mockModuleRef; const mockConfig = { enabled: true, environment: 'test', devtools: { enabled: true, port: 8001, features: { dependencyGraph: true, interactivePlayground: true, performanceMetrics: true, }, }, storage: { driver: 'memory', retention: { hours: 24, maxEntries: 1000 }, batch: { enabled: true, size: 50, flushInterval: 5000 }, }, dashboard: { enabled: true, path: '/telescope', strategy: 'hybrid', }, features: { realTimeUpdates: true, analytics: false, customWatchers: true, }, watchers: { 'test-watcher': { enabled: true, priority: 100 }, 'disabled-watcher': { enabled: false, priority: 200 }, }, }; beforeEach(async () => { const mockEntryManagerService = { process: jest.fn(), processBatch: jest.fn(), }; const mockModuleRefService = { get: jest.fn(), }; const module = await testing_1.Test.createTestingModule({ providers: [ watcher_registry_service_1.WatcherRegistryService, { provide: enhanced_entry_manager_service_1.EnhancedEntryManagerService, useValue: mockEntryManagerService, }, { provide: core_1.ModuleRef, useValue: mockModuleRefService, }, { provide: 'TELESCOPE_CONFIG', useValue: mockConfig, }, ], }).compile(); service = module.get(watcher_registry_service_1.WatcherRegistryService); mockEntryManager = module.get(enhanced_entry_manager_service_1.EnhancedEntryManagerService); mockModuleRef = module.get(core_1.ModuleRef); }); afterEach(() => { jest.clearAllMocks(); }); describe('register', () => { it('should register a watcher', () => { const watcher = new MockWatcher('test-watcher'); service.register(watcher, { priority: 100, tags: ['test'], dependencies: [], }); expect(service.hasWatcher('test-watcher')).toBe(true); expect(service.getWatcher('test-watcher')).toBe(watcher); }); it('should throw error if watcher already exists', () => { const watcher = new MockWatcher('test-watcher'); service.register(watcher); expect(() => service.register(watcher)).toThrow("Watcher 'test-watcher' is already registered"); }); }); describe('unregister', () => { it('should unregister a watcher', () => { const watcher = new MockWatcher('test-watcher'); service.register(watcher); expect(service.hasWatcher('test-watcher')).toBe(true); const result = service.unregister('test-watcher'); expect(result).toBe(true); expect(service.hasWatcher('test-watcher')).toBe(false); }); it('should return false if watcher does not exist', () => { const result = service.unregister('non-existent'); expect(result).toBe(false); }); it('should throw error if watcher has dependents', () => { const watcher1 = new MockWatcher('watcher1'); const watcher2 = new MockWatcher('watcher2'); service.register(watcher1); service.register(watcher2, { dependencies: ['watcher1'] }); expect(() => service.unregister('watcher1')).toThrow("Cannot unregister watcher 'watcher1' - it has dependents: watcher2"); }); }); describe('enable', () => { it('should enable a watcher', async () => { const watcher = new MockWatcher('test-watcher'); service.register(watcher); await service.enable('test-watcher'); expect(watcher.isEnabled).toBe(true); }); it('should throw error if watcher does not exist', async () => { await expect(service.enable('non-existent')).rejects.toThrow("Watcher 'non-existent' not found"); }); it('should throw error if dependency is not enabled', async () => { const watcher1 = new MockWatcher('watcher1'); const watcher2 = new MockWatcher('watcher2'); service.register(watcher1); service.register(watcher2, { dependencies: ['watcher1'] }); await expect(service.enable('watcher2')).rejects.toThrow("Dependency 'watcher1' is not enabled for watcher 'watcher2'"); }); }); describe('disable', () => { it('should disable a watcher', async () => { const watcher = new MockWatcher('test-watcher'); service.register(watcher); await service.enable('test-watcher'); expect(watcher.isEnabled).toBe(true); await service.disable('test-watcher'); expect(watcher.isEnabled).toBe(false); }); it('should throw error if watcher does not exist', async () => { await expect(service.disable('non-existent')).rejects.toThrow("Watcher 'non-existent' not found"); }); it('should throw error if watcher has dependents', async () => { const watcher1 = new MockWatcher('watcher1'); const watcher2 = new MockWatcher('watcher2'); service.register(watcher1); service.register(watcher2, { dependencies: ['watcher1'] }); await service.enable('watcher1'); await service.enable('watcher2'); await expect(service.disable('watcher1')).rejects.toThrow("Cannot disable watcher 'watcher1' - it has dependents: watcher2"); }); }); describe('enableAll', () => { it('should enable all watchers respecting dependencies', async () => { const watcher1 = new MockWatcher('watcher1'); const watcher2 = new MockWatcher('watcher2'); const watcher3 = new MockWatcher('watcher3'); service.register(watcher1, { priority: 100 }); service.register(watcher2, { priority: 200, dependencies: ['watcher1'] }); service.register(watcher3, { priority: 300 }); await service.enableAll(); expect(watcher1.isEnabled).toBe(true); expect(watcher2.isEnabled).toBe(true); expect(watcher3.isEnabled).toBe(true); }); }); describe('disableAll', () => { it('should disable all watchers in reverse order', async () => { const watcher1 = new MockWatcher('watcher1'); const watcher2 = new MockWatcher('watcher2'); service.register(watcher1, { priority: 100 }); service.register(watcher2, { priority: 200, dependencies: ['watcher1'] }); await service.enableAll(); expect(watcher1.isEnabled).toBe(true); expect(watcher2.isEnabled).toBe(true); await service.disableAll(); expect(watcher1.isEnabled).toBe(false); expect(watcher2.isEnabled).toBe(false); }); }); describe('getWatchersByTag', () => { it('should return watchers with specific tag', () => { const watcher1 = new MockWatcher('watcher1'); const watcher2 = new MockWatcher('watcher2'); const watcher3 = new MockWatcher('watcher3'); service.register(watcher1, { tags: ['http', 'api'] }); service.register(watcher2, { tags: ['database'] }); service.register(watcher3, { tags: ['http'] }); const httpWatchers = service.getWatchersByTag('http'); expect(httpWatchers).toHaveLength(2); expect(httpWatchers.map(w => w.name)).toEqual(['watcher1', 'watcher3']); }); }); describe('getStatistics', () => { it('should return watcher statistics', async () => { const watcher1 = new MockWatcher('watcher1'); const watcher2 = new MockWatcher('watcher2'); service.register(watcher1, { priority: 100 }); service.register(watcher2, { priority: 200 }); await service.enable('watcher1'); const stats = service.getStatistics(); expect(stats.total).toBe(2); expect(stats.enabled).toBe(1); expect(stats.disabled).toBe(1); expect(stats.watchers).toHaveLength(2); }); }); describe('processEntry', () => { it('should process entry through entry manager', async () => { const entry = { id: 'test-entry', type: 'request', familyHash: 'hash123', content: { method: 'GET', url: '/test' }, tags: ['http'], timestamp: new Date(), sequence: 1, }; mockEntryManager.process.mockResolvedValue(undefined); await service.processEntry(entry, 'test-watcher'); expect(mockEntryManager.process).toHaveBeenCalledWith(entry); }); it('should update watcher metadata when processing entry', async () => { const watcher = new MockWatcher('test-watcher'); service.register(watcher); const entry = { id: 'test-entry', type: 'request', familyHash: 'hash123', content: { method: 'GET', url: '/test' }, tags: ['http'], timestamp: new Date(), sequence: 1, }; mockEntryManager.process.mockResolvedValue(undefined); await service.processEntry(entry, 'test-watcher'); const metadata = service.getWatcherMetadata('test-watcher'); expect(metadata?.entriesProcessed).toBe(1); expect(metadata?.lastActivity).toBeInstanceOf(Date); }); }); describe('processBatch', () => { it('should process batch through entry manager', async () => { const entries = [ { id: 'test-entry-1', type: 'request', familyHash: 'hash123', content: { method: 'GET', url: '/test1' }, tags: ['http'], timestamp: new Date(), sequence: 1, }, { id: 'test-entry-2', type: 'request', familyHash: 'hash456', content: { method: 'POST', url: '/test2' }, tags: ['http'], timestamp: new Date(), sequence: 2, }, ]; mockEntryManager.processBatch.mockResolvedValue(undefined); await service.processBatch(entries, 'test-watcher'); expect(mockEntryManager.processBatch).toHaveBeenCalledWith(entries); }); it('should update watcher metadata when processing batch', async () => { const watcher = new MockWatcher('test-watcher'); service.register(watcher); const entries = [ { id: 'test-entry-1', type: 'request', familyHash: 'hash123', content: { method: 'GET', url: '/test1' }, tags: ['http'], timestamp: new Date(), sequence: 1, }, { id: 'test-entry-2', type: 'request', familyHash: 'hash456', content: { method: 'POST', url: '/test2' }, tags: ['http'], timestamp: new Date(), sequence: 2, }, ]; mockEntryManager.processBatch.mockResolvedValue(undefined); await service.processBatch(entries, 'test-watcher'); const metadata = service.getWatcherMetadata('test-watcher'); expect(metadata?.entriesProcessed).toBe(2); expect(metadata?.lastActivity).toBeInstanceOf(Date); }); }); describe('healthCheck', () => { it('should return overall health status', async () => { const watcher1 = new MockWatcher('watcher1'); const watcher2 = new MockWatcher('watcher2'); service.register(watcher1); service.register(watcher2); await service.enable('watcher1'); const health = await service.healthCheck(); expect(health.healthy).toBe(false); expect(health.watchers['watcher1']).toBe(true); expect(health.watchers['watcher2']).toBe(false); expect(health.timestamp).toBeInstanceOf(Date); }); it('should handle watcher health check errors', async () => { class FailingWatcher extends MockWatcher { async healthCheck() { throw new Error('Health check failed'); } } const watcher = new FailingWatcher('failing-watcher'); service.register(watcher); const health = await service.healthCheck(); expect(health.healthy).toBe(false); expect(health.watchers['failing-watcher']).toBe(false); }); }); describe('cleanup', () => { it('should cleanup all watchers', async () => { const watcher1 = new MockWatcher('watcher1'); const watcher2 = new MockWatcher('watcher2'); const cleanup1 = jest.spyOn(watcher1, 'cleanup'); const cleanup2 = jest.spyOn(watcher2, 'cleanup'); service.register(watcher1); service.register(watcher2); await service.enableAll(); await service.cleanup(); expect(cleanup1).toHaveBeenCalled(); expect(cleanup2).toHaveBeenCalled(); expect(service.getWatcherCount()).toBe(0); }); }); }); //# sourceMappingURL=watcher-registry.service.spec.js.map