network-performance-analyzer
Version:
Automated analysis tool for network performance test datasets containing DNS testing results and iperf3 performance measurements
391 lines (308 loc) • 13.5 kB
text/typescript
// Tests for PluginManager
import { PluginManager, Plugin, PluginContext } from '../../src/plugins/PluginManager';
import { ConfigurationManager } from '../../src/config/ConfigurationManager';
import fs from 'fs-extra';
import path from 'path';
// Mock fs-extra
jest.mock('fs-extra', () => ({
pathExists: jest.fn().mockResolvedValue(true),
readdir: jest.fn().mockResolvedValue(['plugin1.js', 'plugin2.js', 'not-a-plugin.txt'])
}));
// Mock ConfigurationManager
jest.mock('../../src/config/ConfigurationManager');
// Mock require function
const mockRequire = jest.fn();
jest.mock('module', () => {
const originalModule = jest.requireActual('module');
return {
...originalModule,
_load: mockRequire
};
});
// Mock plugins
class MockPlugin1 implements Plugin {
name = 'mock-plugin-1';
description = 'Mock plugin 1 for testing';
version = '1.0.0';
async initialize(config: any): Promise<void> {}
async execute(context: PluginContext): Promise<any> {
return { result: 'plugin1-result' };
}
}
class MockPlugin2 implements Plugin {
name = 'mock-plugin-2';
description = 'Mock plugin 2 for testing';
version = '1.0.0';
async initialize(config: any): Promise<void> {}
async execute(context: PluginContext): Promise<any> {
return { result: 'plugin2-result' };
}
}
describe('PluginManager', () => {
let pluginManager: PluginManager;
let mockConfigManager: jest.Mocked<ConfigurationManager>;
beforeEach(() => {
jest.clearAllMocks();
// Setup mock ConfigurationManager
mockConfigManager = new ConfigurationManager() as jest.Mocked<ConfigurationManager>;
mockConfigManager.getSection = jest.fn().mockReturnValue({
enabled: ['mock-plugin-1'],
config: {
'mock-plugin-1': { option1: 'value1' }
}
});
pluginManager = new PluginManager(mockConfigManager);
});
describe('addPluginDirectory', () => {
it('should add a plugin directory', () => {
pluginManager.addPluginDirectory('/plugins');
pluginManager.addPluginDirectory('/more-plugins');
// Add the same directory again should not duplicate
pluginManager.addPluginDirectory('/plugins');
// @ts-ignore - Accessing private property for testing
expect(pluginManager.pluginDirectories).toEqual(['/plugins', '/more-plugins']);
});
});
describe('discoverPlugins', () => {
it('should discover plugins from registered directories', async () => {
// Setup mock plugin modules
const mockPlugin1 = new MockPlugin1();
const mockPlugin2 = new MockPlugin2();
mockRequire.mockImplementation((path: string) => {
if (path.includes('plugin1.js')) {
return {
default: mockPlugin1,
author: 'Test Author',
type: 'analyzer'
};
} else if (path.includes('plugin2.js')) {
return {
default: mockPlugin2,
type: 'reporter'
};
}
return {};
});
pluginManager.addPluginDirectory('/plugins');
const discoveredPlugins = await pluginManager.discoverPlugins();
expect(fs.pathExists).toHaveBeenCalledWith('/plugins');
expect(fs.readdir).toHaveBeenCalledWith('/plugins');
expect(discoveredPlugins).toHaveLength(2);
expect(discoveredPlugins[0].name).toBe('mock-plugin-1');
expect(discoveredPlugins[0].type).toBe('analyzer');
expect(discoveredPlugins[0].author).toBe('Test Author');
expect(discoveredPlugins[1].name).toBe('mock-plugin-2');
expect(discoveredPlugins[1].type).toBe('reporter');
});
it('should handle errors when discovering plugins', async () => {
const consoleSpy = jest.spyOn(console, 'error').mockImplementation();
(fs.pathExists as jest.Mock).mockRejectedValueOnce(new Error('Directory error'));
pluginManager.addPluginDirectory('/error-plugins');
const discoveredPlugins = await pluginManager.discoverPlugins();
expect(discoveredPlugins).toEqual([]);
expect(consoleSpy).toHaveBeenCalledWith(
'Error discovering plugins in /error-plugins:',
expect.any(Error)
);
consoleSpy.mockRestore();
});
it('should handle errors when loading individual plugins', async () => {
const consoleSpy = jest.spyOn(console, 'error').mockImplementation();
mockRequire.mockImplementationOnce(() => {
throw new Error('Plugin load error');
});
pluginManager.addPluginDirectory('/plugins');
await pluginManager.discoverPlugins();
expect(consoleSpy).toHaveBeenCalledWith(
'Error loading plugin from /plugins/plugin1.js:',
expect.any(Error)
);
consoleSpy.mockRestore();
});
});
describe('registerPlugin', () => {
it('should register a plugin', () => {
const mockPlugin = new MockPlugin1();
pluginManager.registerPlugin(mockPlugin, { enabled: true, config: { option1: 'value1' } });
// @ts-ignore - Accessing private property for testing
expect(pluginManager.plugins.get('mock-plugin-1')).toBe(mockPlugin);
// @ts-ignore - Accessing private property for testing
expect(pluginManager.pluginMetadata.get('mock-plugin-1')).toEqual({
name: 'mock-plugin-1',
description: 'Mock plugin 1 for testing',
version: '1.0.0',
type: 'utility',
path: 'custom',
enabled: true
});
// @ts-ignore - Accessing private property for testing
expect(pluginManager.pluginConfigs.get('mock-plugin-1')).toEqual({ option1: 'value1' });
});
it('should update existing plugin metadata if already registered', () => {
const mockPlugin = new MockPlugin1();
// Register with initial metadata
pluginManager.registerPlugin(mockPlugin, { enabled: false });
// Register again with different enabled status
pluginManager.registerPlugin(mockPlugin, { enabled: true });
// @ts-ignore - Accessing private property for testing
expect(pluginManager.pluginMetadata.get('mock-plugin-1')?.enabled).toBe(true);
});
it('should throw an error for invalid plugins', () => {
const invalidPlugin = {
name: 'invalid-plugin',
// Missing required properties and methods
};
expect(() => {
// @ts-ignore - Testing with invalid plugin
pluginManager.registerPlugin(invalidPlugin);
}).toThrow('Invalid plugin: invalid-plugin');
});
});
describe('enablePlugin and disablePlugin', () => {
it('should enable a plugin', () => {
const mockPlugin = new MockPlugin1();
pluginManager.registerPlugin(mockPlugin, { enabled: false });
pluginManager.enablePlugin('mock-plugin-1');
// @ts-ignore - Accessing private property for testing
expect(pluginManager.pluginMetadata.get('mock-plugin-1')?.enabled).toBe(true);
expect(mockConfigManager.update).toHaveBeenCalled();
});
it('should disable a plugin', () => {
const mockPlugin = new MockPlugin1();
pluginManager.registerPlugin(mockPlugin, { enabled: true });
pluginManager.disablePlugin('mock-plugin-1');
// @ts-ignore - Accessing private property for testing
expect(pluginManager.pluginMetadata.get('mock-plugin-1')?.enabled).toBe(false);
expect(mockConfigManager.update).toHaveBeenCalled();
});
});
describe('getPlugins, getEnabledPlugins, and getPluginsByType', () => {
beforeEach(() => {
const mockPlugin1 = new MockPlugin1();
const mockPlugin2 = new MockPlugin2();
// @ts-ignore - Setting private properties for testing
pluginManager.pluginMetadata.set('mock-plugin-1', {
name: 'mock-plugin-1',
description: 'Mock plugin 1 for testing',
version: '1.0.0',
type: 'analyzer',
path: 'custom',
enabled: true
});
// @ts-ignore - Setting private properties for testing
pluginManager.pluginMetadata.set('mock-plugin-2', {
name: 'mock-plugin-2',
description: 'Mock plugin 2 for testing',
version: '1.0.0',
type: 'reporter',
path: 'custom',
enabled: false
});
});
it('should get all plugins', () => {
const plugins = pluginManager.getPlugins();
expect(plugins).toHaveLength(2);
expect(plugins[0].name).toBe('mock-plugin-1');
expect(plugins[1].name).toBe('mock-plugin-2');
});
it('should get enabled plugins', () => {
const enabledPlugins = pluginManager.getEnabledPlugins();
expect(enabledPlugins).toHaveLength(1);
expect(enabledPlugins[0].name).toBe('mock-plugin-1');
});
it('should get plugins by type', () => {
const analyzerPlugins = pluginManager.getPluginsByType('analyzer');
const reporterPlugins = pluginManager.getPluginsByType('reporter');
expect(analyzerPlugins).toHaveLength(1);
expect(analyzerPlugins[0].name).toBe('mock-plugin-1');
expect(reporterPlugins).toHaveLength(1);
expect(reporterPlugins[0].name).toBe('mock-plugin-2');
});
});
describe('executePlugins', () => {
it('should execute enabled plugins of a specific type', async () => {
const mockPlugin1 = new MockPlugin1();
const mockPlugin2 = new MockPlugin2();
const initializeSpy1 = jest.spyOn(mockPlugin1, 'initialize');
const executeSpy1 = jest.spyOn(mockPlugin1, 'execute');
const initializeSpy2 = jest.spyOn(mockPlugin2, 'initialize');
const executeSpy2 = jest.spyOn(mockPlugin2, 'execute');
// Register plugins
pluginManager.registerPlugin(mockPlugin1, { enabled: true, config: { option1: 'value1' } });
pluginManager.registerPlugin(mockPlugin2, { enabled: false });
// @ts-ignore - Setting private properties for testing
pluginManager.pluginMetadata.get('mock-plugin-1')!.type = 'analyzer';
// @ts-ignore - Setting private properties for testing
pluginManager.pluginMetadata.get('mock-plugin-2')!.type = 'analyzer';
const context: PluginContext = { datasets: [] };
const results = await pluginManager.executePlugins('analyzer', context);
expect(results).toHaveLength(1);
expect(results[0]).toEqual({ result: 'plugin1-result' });
expect(initializeSpy1).toHaveBeenCalledWith({ option1: 'value1' });
expect(executeSpy1).toHaveBeenCalledWith(context);
expect(initializeSpy2).not.toHaveBeenCalled();
expect(executeSpy2).not.toHaveBeenCalled();
});
it('should handle errors when executing plugins', async () => {
const consoleSpy = jest.spyOn(console, 'error').mockImplementation();
const mockPlugin = new MockPlugin1();
jest.spyOn(mockPlugin, 'execute').mockImplementation(() => {
throw new Error('Plugin execution error');
});
pluginManager.registerPlugin(mockPlugin, { enabled: true });
// @ts-ignore - Setting private properties for testing
pluginManager.pluginMetadata.get('mock-plugin-1')!.type = 'analyzer';
const results = await pluginManager.executePlugins('analyzer', { datasets: [] });
expect(results).toEqual([]);
expect(consoleSpy).toHaveBeenCalledWith(
'Error executing plugin mock-plugin-1:',
expect.any(Error)
);
consoleSpy.mockRestore();
});
});
describe('loadEnabledPlugins', () => {
it('should load enabled plugins from configuration', async () => {
// Setup mock plugin modules
const mockPlugin1 = new MockPlugin1();
mockRequire.mockImplementation(() => ({
default: mockPlugin1
}));
// @ts-ignore - Setting private properties for testing
pluginManager.pluginMetadata.set('mock-plugin-1', {
name: 'mock-plugin-1',
description: 'Mock plugin 1 for testing',
version: '1.0.0',
type: 'analyzer',
path: '/plugins/mock-plugin-1.js',
enabled: false
});
await pluginManager.loadEnabledPlugins();
// @ts-ignore - Accessing private property for testing
expect(pluginManager.plugins.get('mock-plugin-1')).toBeDefined();
// @ts-ignore - Accessing private property for testing
expect(pluginManager.pluginMetadata.get('mock-plugin-1')?.enabled).toBe(true);
});
it('should handle errors when loading plugins', async () => {
const consoleSpy = jest.spyOn(console, 'error').mockImplementation();
mockRequire.mockImplementation(() => {
throw new Error('Plugin load error');
});
// @ts-ignore - Setting private properties for testing
pluginManager.pluginMetadata.set('mock-plugin-1', {
name: 'mock-plugin-1',
description: 'Mock plugin 1 for testing',
version: '1.0.0',
type: 'analyzer',
path: '/plugins/mock-plugin-1.js',
enabled: false
});
await pluginManager.loadEnabledPlugins();
expect(consoleSpy).toHaveBeenCalledWith(
'Error loading plugin mock-plugin-1:',
expect.any(Error)
);
consoleSpy.mockRestore();
});
});
});